mac/libusb/strerror.c
mac/libusb/sync.c
mac/libusb/os/darwin_usb.c
- mac/libusb/os/poll_posix.c
+ mac/libusb/os/events_posix.c
mac/libusb/os/threads_posix.c
)
set(HEADERS ${HEADERS}
mac/libusb/version.h
mac/libusb/version_nano.h
mac/libusb/os/darwin_usb.h
- mac/libusb/os/poll_posix.h
+ mac/libusb/os/events_posix.h
mac/libusb/os/threads_posix.h
)
add_compile_options(-Wall -Wsign-compare)
mac/libusb/strerror.c \
mac/libusb/sync.c \
mac/libusb/os/darwin_usb.c \
- mac/libusb/os/poll_posix.c \
+ mac/libusb/os/events_posix.c \
mac/libusb/os/threads_posix.c
HEADERS += mac/libusb/hotplug.h \
mac/libusb/libusb.h \
mac/libusb/version.h \
mac/libusb/version_nano.h \
mac/libusb/os/darwin_usb.h \
- mac/libusb/os/poll_posix.h \
+ mac/libusb/os/events_posix.h \
mac/libusb/os/threads_posix.h
} else:equals(WITH_LIBUSB, custom) {
message("libusb-1.0 is enabled but but must be manually configured")
-This is libusb-1.0.22 from https://libusb.info/.
+This is libusb-1.0.24 from https://libusb.info/.
Since we have such problems with people
getting libusb successfully built - between the Universal Build issues
and the fact that we have to work hard to go find where it's installed
/* config.h. Manually generated for Xcode. */
-/* Default visibility */
-#define DEFAULT_VISIBILITY /**/
+#include <AvailabilityMacros.h>
-/* Message logging */
+/* Define to the attribute for default visibility. */
+#define DEFAULT_VISIBILITY __attribute__ ((visibility ("default")))
+
+/* Define to 1 to enable message logging. */
#define ENABLE_LOGGING 1
-/* Define to 1 if you have the <poll.h> header file. */
-#define HAVE_POLL_H 1
+/* On 10.12 and later, use newly available clock_*() functions */
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+/* Define to 1 if you have the `clock_gettime' function. */
+#define HAVE_CLOCK_GETTIME 1
+#endif
+
+/* On 10.6 and later, use newly available pthread_threadid_np() function */
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
+/* Define to 1 if you have the 'pthread_threadid_np' function. */
+#define HAVE_PTHREAD_THREADID_NP 1
+#endif
+
+/* Define to 1 if the system has the type `nfds_t'. */
+#define HAVE_NFDS_T 1
/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
-/* Darwin backend */
-#define OS_DARWIN 1
-
-/* type of second poll() argument */
-#define POLL_NFDS_TYPE nfds_t
+/* Define to 1 if compiling for a POSIX platform. */
+#define PLATFORM_POSIX 1
-/* Use POSIX Threads */
-#define THREADS_POSIX 1
+/* Define to the attribute for enabling parameter checks on printf-like
+ functions. */
+#define PRINTF_FORMAT(a, b) __attribute__ ((__format__ (__printf__, a, b)))
-/* Use GNU extensions */
+/* Enable GNU extensions. */
#define _GNU_SOURCE 1
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "config.h"
+#include "libusbi.h"
+#include "hotplug.h"
+#include "version.h"
-#include <errno.h>
-#include <stdarg.h>
+#ifdef __ANDROID__
+#include <android/log.h>
+#endif
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#ifdef HAVE_SYSLOG_H
+#ifdef HAVE_SYSLOG
#include <syslog.h>
#endif
-#ifdef __ANDROID__
-#include <android/log.h>
-#endif
-
-#include "libusbi.h"
-#include "hotplug.h"
-
-struct libusb_context *usbi_default_context = NULL;
+struct libusb_context *usbi_default_context;
static const struct libusb_version libusb_version_internal =
{ LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO,
LIBUSB_RC, "http://libusb.info" };
-static int default_context_refcnt = 0;
+static int default_context_refcnt;
static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
-static struct timespec timestamp_origin = { 0, 0 };
+static struct timespec timestamp_origin;
+#if defined(ENABLE_LOGGING) && !defined(USE_SYSTEM_LOGGING_FACILITY)
+static libusb_log_cb log_handler;
+#endif
usbi_mutex_static_t active_contexts_lock = USBI_MUTEX_INITIALIZER;
struct list_head active_contexts_list;
* \section intro Introduction
*
* libusb is an open source library that allows you to communicate with USB
- * devices from userspace. For more info, see the
+ * devices from user space. For more info, see the
* <a href="http://libusb.info">libusb homepage</a>.
*
* This documentation is aimed at application developers wishing to
/**
* \page libusb_caveats Caveats
*
+ * \section threadsafety Thread safety
+ *
+ * libusb is designed to be completely thread-safe, but as with any API it
+ * cannot prevent a user from sabotaging themselves, either intentionally or
+ * otherwise.
+ *
+ * Observe the following general guidelines:
+ *
+ * - Calls to functions that release a resource (e.g. libusb_close(),
+ * libusb_free_config_descriptor()) should not be called concurrently on
+ * the same resource. This is no different than concurrently calling free()
+ * on the same allocated pointer.
+ * - Each individual \ref libusb_transfer should be prepared by a single
+ * thread. In other words, no two threads should ever be concurrently
+ * filling out the fields of a \ref libusb_transfer. You can liken this to
+ * calling sprintf() with the same destination buffer from multiple threads.
+ * The results will likely not be what you want unless the input parameters
+ * are all the same, but its best to avoid this situation entirely.
+ * - Both the \ref libusb_transfer structure and its associated data buffer
+ * should not be accessed between the time the transfer is submitted and the
+ * time the completion callback is invoked. You can think of "ownership" of
+ * these things as being transferred to libusb while the transfer is active.
+ * - The various "setter" functions (e.g. libusb_set_log_cb(),
+ * libusb_set_pollfd_notifiers()) should not be called concurrently on the
+ * resource. Though doing so will not lead to any undefined behavior, it
+ * will likely produce results that the application does not expect.
+ *
+ * Rules for multiple threads and asynchronous I/O are detailed
+ * \ref libusb_mtasync "here".
+ *
* \section fork Fork considerations
*
* libusb is <em>not</em> designed to work across fork() calls. Depending on
* you when this has happened, so if someone else resets your device it will
* not be clear to your own program why the device state has changed.
*
- * Ultimately, this is a limitation of writing drivers in userspace.
+ * Ultimately, this is a limitation of writing drivers in user space.
* Separation from the USB stack in the underlying kernel makes it difficult
* for the operating system to deliver such notifications to your program.
* The Linux kernel USB stack allows such reset notifications to be delivered
* to in-kernel USB drivers, but it is not clear how such notifications could
- * be delivered to second-class drivers that live in userspace.
+ * be delivered to second-class drivers that live in user space.
*
* \section blockonly Blocking-only functionality
*
* - libusb_set_auto_detach_kernel_driver()
* - libusb_set_configuration()
* - libusb_set_debug()
+ * - libusb_set_log_cb()
* - libusb_set_interface_alt_setting()
* - libusb_set_iso_packet_lengths()
* - libusb_set_option()
* - libusb_unlock_event_waiters()
* - libusb_unref_device()
* - libusb_wait_for_event()
+ * - libusb_wrap_sys_device()
*
* \section Structures
* - libusb_bos_descriptor
* - \ref libusb_class_code
* - \ref libusb_descriptor_type
* - \ref libusb_endpoint_direction
+ * - \ref libusb_endpoint_transfer_type
* - \ref libusb_error
* - \ref libusb_iso_sync_type
* - \ref libusb_iso_usage_type
unsigned long session_id)
{
size_t priv_size = usbi_backend.device_priv_size;
- struct libusb_device *dev = calloc(1, sizeof(*dev) + priv_size);
- int r;
+ struct libusb_device *dev = calloc(1, PTR_ALIGN(sizeof(*dev)) + priv_size);
if (!dev)
return NULL;
- r = usbi_mutex_init(&dev->lock);
- if (r) {
- free(dev);
- return NULL;
- }
+ usbi_mutex_init(&dev->lock);
dev->ctx = ctx;
dev->refcnt = 1;
* to the discovered device list. */
int usbi_sanitize_device(struct libusb_device *dev)
{
- int r;
uint8_t num_configurations;
- r = usbi_device_cache_descriptor(dev);
- if (r < 0)
- return r;
+ if (dev->device_descriptor.bLength != LIBUSB_DT_DEVICE_SIZE ||
+ dev->device_descriptor.bDescriptorType != LIBUSB_DT_DEVICE) {
+ usbi_err(DEVICE_CTX(dev), "invalid device descriptor");
+ return LIBUSB_ERROR_IO;
+ }
num_configurations = dev->device_descriptor.bNumConfigurations;
if (num_configurations > USB_MAXCONFIG) {
usbi_err(DEVICE_CTX(dev), "too many configurations");
return LIBUSB_ERROR_IO;
- } else if (0 == num_configurations)
+ } else if (0 == num_configurations) {
usbi_dbg("zero configurations, maybe an unauthorized device");
+ }
- dev->num_configurations = num_configurations;
return 0;
}
struct libusb_device *ret = NULL;
usbi_mutex_lock(&ctx->usb_devs_lock);
- list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device)
+ for_each_device(ctx, dev) {
if (dev->session_data == session_id) {
ret = libusb_ref_device(dev);
break;
}
+ }
usbi_mutex_unlock(&ctx->usb_devs_lock);
return ret;
struct libusb_device **ret;
int r = 0;
ssize_t i, len;
- USBI_GET_CONTEXT(ctx);
- usbi_dbg("");
+
+ usbi_dbg(" ");
if (!discdevs)
return LIBUSB_ERROR_NO_MEM;
+ ctx = usbi_get_context(ctx);
+
if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
/* backend provides hotplug support */
struct libusb_device *dev;
usbi_backend.hotplug_poll();
usbi_mutex_lock(&ctx->usb_devs_lock);
- list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) {
+ for_each_device(ctx, dev) {
discdevs = discovered_devs_append(discdevs, dev);
if (!discdevs) {
}
/* convert discovered_devs into a list */
- len = discdevs->len;
- ret = calloc(len + 1, sizeof(struct libusb_device *));
+ len = (ssize_t)discdevs->len;
+ ret = calloc((size_t)len + 1, sizeof(struct libusb_device *));
if (!ret) {
len = LIBUSB_ERROR_NO_MEM;
goto out;
* \returns LIBUSB_ERROR_OVERFLOW if the array is too small
*/
int API_EXPORTED libusb_get_port_numbers(libusb_device *dev,
- uint8_t* port_numbers, int port_numbers_len)
+ uint8_t *port_numbers, int port_numbers_len)
{
int i = port_numbers_len;
struct libusb_context *ctx = DEVICE_CTX(dev);
}
/** \ingroup libusb_dev
- * Deprecated please use libusb_get_port_numbers instead.
+ * \deprecated Please use \ref libusb_get_port_numbers() instead.
*/
int API_EXPORTED libusb_get_port_path(libusb_context *ctx, libusb_device *dev,
- uint8_t* port_numbers, uint8_t port_numbers_len)
+ uint8_t *port_numbers, uint8_t port_numbers_len)
{
UNUSED(ctx);
* function and make sure that you only access the parent before issuing
* \ref libusb_free_device_list(). The reason is that libusb currently does
* not maintain a permanent list of device instances, and therefore can
- * only guarantee that parents are fully instantiated within a
+ * only guarantee that parents are fully instantiated within a
* libusb_get_device_list() - libusb_free_device_list() block.
*/
DEFAULT_VISIBILITY
* If acting on an isochronous or interrupt endpoint, this function will
* multiply the value found in bits 0:10 by the number of transactions per
* microframe (determined by bits 11:12). Otherwise, this function just
- * returns the numeric value found in bits 0:10.
+ * returns the numeric value found in bits 0:10. For USB 3.0 device, it
+ * will attempts to retrieve the Endpoint Companion Descriptor to return
+ * wBytesPerInterval.
*
* This function is useful for setting up isochronous transfers, for example
* you might pass the return value from this function to
{
struct libusb_config_descriptor *config;
const struct libusb_endpoint_descriptor *ep;
- enum libusb_transfer_type ep_type;
+ struct libusb_ss_endpoint_companion_descriptor *ss_ep_cmp;
+ enum libusb_endpoint_transfer_type ep_type;
uint16_t val;
int r;
+ int speed;
r = libusb_get_active_config_descriptor(dev, &config);
if (r < 0) {
goto out;
}
- val = ep->wMaxPacketSize;
- ep_type = (enum libusb_transfer_type) (ep->bmAttributes & 0x3);
+ speed = libusb_get_device_speed(dev);
+ if (speed >= LIBUSB_SPEED_SUPER) {
+ r = libusb_get_ss_endpoint_companion_descriptor(dev->ctx, ep, &ss_ep_cmp);
+ if (r == LIBUSB_SUCCESS) {
+ r = ss_ep_cmp->wBytesPerInterval;
+ libusb_free_ss_endpoint_companion_descriptor(ss_ep_cmp);
+ }
+ }
- r = val & 0x07ff;
- if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
- || ep_type == LIBUSB_TRANSFER_TYPE_INTERRUPT)
- r *= (1 + ((val >> 11) & 3));
+ /* If the device isn't a SuperSpeed device or retrieving the SS endpoint didn't worked. */
+ if (speed < LIBUSB_SPEED_SUPER || r < 0) {
+ val = ep->wMaxPacketSize;
+ ep_type = (enum libusb_endpoint_transfer_type) (ep->bmAttributes & 0x3);
+
+ r = val & 0x07ff;
+ if (ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS
+ || ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT)
+ r *= (1 + ((val >> 11) & 3));
+ }
out:
libusb_free_config_descriptor(config);
}
}
-/*
- * Signal the event pipe so that the event handling thread will be
- * interrupted to process an internal event.
+/** \ingroup libusb_dev
+ * Wrap a platform-specific system device handle and obtain a libusb device
+ * handle for the underlying device. The handle allows you to use libusb to
+ * perform I/O on the device in question.
+ *
+ * Must call libusb_set_option(NULL, LIBUSB_OPTION_WEAK_AUTHORITY)
+ * before libusb_init if don't have authority to access the usb device directly.
+ *
+ * On Linux, the system device handle must be a valid file descriptor opened
+ * on the device node.
+ *
+ * The system device handle must remain open until libusb_close() is called.
+ * The system device handle will not be closed by libusb_close().
+ *
+ * Internally, this function creates a temporary device and makes it
+ * available to you through libusb_get_device(). This device is destroyed
+ * during libusb_close(). The device shall not be opened through libusb_open().
+ *
+ * This is a non-blocking function; no requests are sent over the bus.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param sys_dev the platform-specific system device handle
+ * \param dev_handle output location for the returned device handle pointer. Only
+ * populated when the return code is 0.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NO_MEM on memory allocation failure
+ * \returns LIBUSB_ERROR_ACCESS if the user has insufficient permissions
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED if the operation is not supported on this
+ * platform
+ * \returns another LIBUSB_ERROR code on other failure
*/
-int usbi_signal_event(struct libusb_context *ctx)
+int API_EXPORTED libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev,
+ libusb_device_handle **dev_handle)
{
- unsigned char dummy = 1;
- ssize_t r;
+ struct libusb_device_handle *_dev_handle;
+ size_t priv_size = usbi_backend.device_handle_priv_size;
+ int r;
- /* write some data on event pipe to interrupt event handlers */
- r = usbi_write(ctx->event_pipe[1], &dummy, sizeof(dummy));
- if (r != sizeof(dummy)) {
- usbi_warn(ctx, "internal signalling write failed");
- return LIBUSB_ERROR_IO;
- }
+ usbi_dbg("wrap_sys_device 0x%" PRIxPTR, (uintptr_t)sys_dev);
- return 0;
-}
+ ctx = usbi_get_context(ctx);
-/*
- * Clear the event pipe so that the event handling will no longer be
- * interrupted.
- */
-int usbi_clear_event(struct libusb_context *ctx)
-{
- unsigned char dummy;
- ssize_t r;
+ if (!usbi_backend.wrap_sys_device)
+ return LIBUSB_ERROR_NOT_SUPPORTED;
- /* read some data on event pipe to clear it */
- r = usbi_read(ctx->event_pipe[0], &dummy, sizeof(dummy));
- if (r != sizeof(dummy)) {
- usbi_warn(ctx, "internal signalling read failed");
- return LIBUSB_ERROR_IO;
+ _dev_handle = calloc(1, PTR_ALIGN(sizeof(*_dev_handle)) + priv_size);
+ if (!_dev_handle)
+ return LIBUSB_ERROR_NO_MEM;
+
+ usbi_mutex_init(&_dev_handle->lock);
+
+ r = usbi_backend.wrap_sys_device(ctx, _dev_handle, sys_dev);
+ if (r < 0) {
+ usbi_dbg("wrap_sys_device 0x%" PRIxPTR " returns %d", (uintptr_t)sys_dev, r);
+ usbi_mutex_destroy(&_dev_handle->lock);
+ free(_dev_handle);
+ return r;
}
+ usbi_mutex_lock(&ctx->open_devs_lock);
+ list_add(&_dev_handle->list, &ctx->open_devs);
+ usbi_mutex_unlock(&ctx->open_devs_lock);
+ *dev_handle = _dev_handle;
+
return 0;
}
return LIBUSB_ERROR_NO_DEVICE;
}
- _dev_handle = malloc(sizeof(*_dev_handle) + priv_size);
+ _dev_handle = calloc(1, PTR_ALIGN(sizeof(*_dev_handle)) + priv_size);
if (!_dev_handle)
return LIBUSB_ERROR_NO_MEM;
- r = usbi_mutex_init(&_dev_handle->lock);
- if (r) {
- free(_dev_handle);
- return LIBUSB_ERROR_OTHER;
- }
+ usbi_mutex_init(&_dev_handle->lock);
_dev_handle->dev = libusb_ref_device(dev);
- _dev_handle->auto_detach_kernel_driver = 0;
- _dev_handle->claimed_interfaces = 0;
- memset(&_dev_handle->os_priv, 0, priv_size);
r = usbi_backend.open(_dev_handle);
if (r < 0) {
usbi_mutex_lock(&ctx->flying_transfers_lock);
/* safe iteration because transfers may be being deleted */
- list_for_each_entry_safe(itransfer, tmp, &ctx->flying_transfers, list, struct usbi_transfer) {
+ for_each_transfer_safe(ctx, itransfer, tmp) {
struct libusb_transfer *transfer =
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
{
struct libusb_context *ctx;
+ unsigned int event_flags;
int handling_events;
- int pending_events;
if (!dev_handle)
return;
- usbi_dbg("");
+ usbi_dbg(" ");
ctx = HANDLE_CTX(dev_handle);
handling_events = usbi_handling_events(ctx);
/* Record that we are closing a device.
* Only signal an event if there are no prior pending events. */
usbi_mutex_lock(&ctx->event_data_lock);
- pending_events = usbi_pending_events(ctx);
- ctx->device_close++;
- if (!pending_events)
- usbi_signal_event(ctx);
+ event_flags = ctx->event_flags;
+ if (!ctx->device_close++)
+ ctx->event_flags |= USBI_EVENT_DEVICE_CLOSE;
+ if (!event_flags)
+ usbi_signal_event(&ctx->event);
usbi_mutex_unlock(&ctx->event_data_lock);
/* take event handling lock */
/* We're done with closing this device.
* Clear the event pipe if there are no further pending events. */
usbi_mutex_lock(&ctx->event_data_lock);
- ctx->device_close--;
- pending_events = usbi_pending_events(ctx);
- if (!pending_events)
- usbi_clear_event(ctx);
+ if (!--ctx->device_close)
+ ctx->event_flags &= ~USBI_EVENT_DEVICE_CLOSE;
+ if (!ctx->event_flags)
+ usbi_clear_event(&ctx->event);
usbi_mutex_unlock(&ctx->event_data_lock);
/* Release event handling lock and wake up event waiters */
int *config)
{
int r = LIBUSB_ERROR_NOT_SUPPORTED;
+ uint8_t tmp = 0;
- usbi_dbg("");
+ usbi_dbg(" ");
if (usbi_backend.get_configuration)
- r = usbi_backend.get_configuration(dev_handle, config);
+ r = usbi_backend.get_configuration(dev_handle, &tmp);
if (r == LIBUSB_ERROR_NOT_SUPPORTED) {
- uint8_t tmp = 0;
usbi_dbg("falling back to control message");
r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN,
LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000);
- if (r == 0) {
+ if (r == 1) {
+ r = 0;
+ } else if (r == 0) {
usbi_err(HANDLE_CTX(dev_handle), "zero bytes returned in ctrl transfer?");
r = LIBUSB_ERROR_IO;
- } else if (r == 1) {
- r = 0;
- *config = tmp;
} else {
usbi_dbg("control failed, error %d", r);
}
}
- if (r == 0)
- usbi_dbg("active config %d", *config);
+ if (r == 0) {
+ usbi_dbg("active config %u", tmp);
+ *config = (int)tmp;
+ }
return r;
}
* causing most USB-related device state to be reset (altsetting reset to zero,
* endpoint halts cleared, toggles reset).
*
+ * Not all backends support setting the configuration from user space, which
+ * will be indicated by the return code LIBUSB_ERROR_NOT_SUPPORTED. As this
+ * suggests that the platform is handling the device configuration itself,
+ * this error should generally be safe to ignore.
+ *
* You cannot change/reset configuration if your application has claimed
* interfaces. It is advised to set the desired configuration before claiming
* interfaces.
* \returns 0 on success
* \returns LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist
* \returns LIBUSB_ERROR_BUSY if interfaces are currently claimed
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED if setting or changing the configuration
+ * is not supported by the backend
* \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other failure
* \see libusb_set_auto_detach_kernel_driver()
int configuration)
{
usbi_dbg("configuration %d", configuration);
+ if (configuration < -1 || configuration > (int)UINT8_MAX)
+ return LIBUSB_ERROR_INVALID_PARAM;
return usbi_backend.set_configuration(dev_handle, configuration);
}
int r = 0;
usbi_dbg("interface %d", interface_number);
- if (interface_number >= USB_MAXINTERFACES)
+ if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
if (!dev_handle->dev->attached)
return LIBUSB_ERROR_NO_DEVICE;
usbi_mutex_lock(&dev_handle->lock);
- if (dev_handle->claimed_interfaces & (1 << interface_number))
+ if (dev_handle->claimed_interfaces & (1U << interface_number))
goto out;
- r = usbi_backend.claim_interface(dev_handle, interface_number);
+ r = usbi_backend.claim_interface(dev_handle, (uint8_t)interface_number);
if (r == 0)
- dev_handle->claimed_interfaces |= 1 << interface_number;
+ dev_handle->claimed_interfaces |= 1U << interface_number;
out:
usbi_mutex_unlock(&dev_handle->lock);
int r;
usbi_dbg("interface %d", interface_number);
- if (interface_number >= USB_MAXINTERFACES)
+ if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
usbi_mutex_lock(&dev_handle->lock);
- if (!(dev_handle->claimed_interfaces & (1 << interface_number))) {
+ if (!(dev_handle->claimed_interfaces & (1U << interface_number))) {
r = LIBUSB_ERROR_NOT_FOUND;
goto out;
}
- r = usbi_backend.release_interface(dev_handle, interface_number);
+ r = usbi_backend.release_interface(dev_handle, (uint8_t)interface_number);
if (r == 0)
- dev_handle->claimed_interfaces &= ~(1 << interface_number);
+ dev_handle->claimed_interfaces &= ~(1U << interface_number);
out:
usbi_mutex_unlock(&dev_handle->lock);
{
usbi_dbg("interface %d altsetting %d",
interface_number, alternate_setting);
- if (interface_number >= USB_MAXINTERFACES)
+ if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
+ return LIBUSB_ERROR_INVALID_PARAM;
+ if (alternate_setting < 0 || alternate_setting > (int)UINT8_MAX)
return LIBUSB_ERROR_INVALID_PARAM;
usbi_mutex_lock(&dev_handle->lock);
return LIBUSB_ERROR_NO_DEVICE;
}
- if (!(dev_handle->claimed_interfaces & (1 << interface_number))) {
+ if (!(dev_handle->claimed_interfaces & (1U << interface_number))) {
usbi_mutex_unlock(&dev_handle->lock);
return LIBUSB_ERROR_NOT_FOUND;
}
usbi_mutex_unlock(&dev_handle->lock);
- return usbi_backend.set_interface_altsetting(dev_handle, interface_number,
- alternate_setting);
+ return usbi_backend.set_interface_altsetting(dev_handle,
+ (uint8_t)interface_number, (uint8_t)alternate_setting);
}
/** \ingroup libusb_dev
*/
int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle)
{
- usbi_dbg("");
+ usbi_dbg(" ");
if (!dev_handle->dev->attached)
return LIBUSB_ERROR_NO_DEVICE;
- return usbi_backend.reset_device(dev_handle);
+ if (usbi_backend.reset_device)
+ return usbi_backend.reset_device(dev_handle);
+ else
+ return LIBUSB_ERROR_NOT_SUPPORTED;
}
/** \ingroup libusb_asyncio
int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle,
uint32_t num_streams, unsigned char *endpoints, int num_endpoints)
{
- usbi_dbg("streams %u eps %d", (unsigned) num_streams, num_endpoints);
+ usbi_dbg("streams %u eps %d", (unsigned)num_streams, num_endpoints);
+
+ if (!num_streams || !endpoints || num_endpoints <= 0)
+ return LIBUSB_ERROR_INVALID_PARAM;
if (!dev_handle->dev->attached)
return LIBUSB_ERROR_NO_DEVICE;
{
usbi_dbg("eps %d", num_endpoints);
+ if (!endpoints || num_endpoints <= 0)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
if (!dev_handle->dev->attached)
return LIBUSB_ERROR_NO_DEVICE;
* the same cache lines) when a transfer is in progress, although it is legal
* to have several transfers going on within the same memory block.
*
- * Will return NULL on failure. Many systems do not support such zerocopy
+ * Will return NULL on failure. Many systems do not support such zero-copy
* and will always return NULL. Memory allocated with this function must be
* freed with \ref libusb_dev_mem_free. Specifically, this means that the
* flag \ref LIBUSB_TRANSFER_FREE_BUFFER cannot be used to free memory allocated
{
usbi_dbg("interface %d", interface_number);
+ if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
if (!dev_handle->dev->attached)
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.kernel_driver_active)
- return usbi_backend.kernel_driver_active(dev_handle, interface_number);
+ return usbi_backend.kernel_driver_active(dev_handle, (uint8_t)interface_number);
else
return LIBUSB_ERROR_NOT_SUPPORTED;
}
{
usbi_dbg("interface %d", interface_number);
+ if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
if (!dev_handle->dev->attached)
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.detach_kernel_driver)
- return usbi_backend.detach_kernel_driver(dev_handle, interface_number);
+ return usbi_backend.detach_kernel_driver(dev_handle, (uint8_t)interface_number);
else
return LIBUSB_ERROR_NOT_SUPPORTED;
}
{
usbi_dbg("interface %d", interface_number);
+ if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
if (!dev_handle->dev->attached)
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.attach_kernel_driver)
- return usbi_backend.attach_kernel_driver(dev_handle, interface_number);
+ return usbi_backend.attach_kernel_driver(dev_handle, (uint8_t)interface_number);
else
return LIBUSB_ERROR_NOT_SUPPORTED;
}
void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
{
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
- USBI_GET_CONTEXT(ctx);
+ ctx = usbi_get_context(ctx);
if (!ctx->debug_fixed) {
level = CLAMP(level, LIBUSB_LOG_LEVEL_NONE, LIBUSB_LOG_LEVEL_DEBUG);
ctx->debug = (enum libusb_log_level)level;
#endif
}
+/** \ingroup libusb_lib
+ * Set log handler.
+ *
+ * libusb will redirect its log messages to the provided callback function.
+ * libusb supports redirection of per context and global log messages.
+ * Log messages sent to the context will be sent to the global log handler too.
+ *
+ * If libusb is compiled without message logging or USE_SYSTEM_LOGGING_FACILITY
+ * is defined then global callback function will never be called.
+ * If ENABLE_DEBUG_LOGGING is defined then per context callback function will
+ * never be called.
+ *
+ * \param ctx context on which to assign log handler, or NULL for the default
+ * context. Parameter ignored if only LIBUSB_LOG_CB_GLOBAL mode is requested.
+ * \param cb pointer to the callback function, or NULL to stop log
+ * messages redirection
+ * \param mode mode of callback function operation. Several modes can be
+ * selected for a single callback function, see \ref libusb_log_cb_mode for
+ * a description.
+ * \see libusb_log_cb, libusb_log_cb_mode
+ */
+void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb,
+ int mode)
+{
+#if defined(ENABLE_LOGGING) && (!defined(ENABLE_DEBUG_LOGGING) || !defined(USE_SYSTEM_LOGGING_FACILITY))
+#if !defined(USE_SYSTEM_LOGGING_FACILITY)
+ if (mode & LIBUSB_LOG_CB_GLOBAL)
+ log_handler = cb;
+#endif
+#if !defined(ENABLE_DEBUG_LOGGING)
+ if (mode & LIBUSB_LOG_CB_CONTEXT) {
+ ctx = usbi_get_context(ctx);
+ ctx->log_handler = cb;
+ }
+#else
+ UNUSED(ctx);
+#endif
+#else
+ UNUSED(ctx);
+ UNUSED(cb);
+ UNUSED(mode);
+#endif
+}
+
/** \ingroup libusb_lib
* Set an option in the library.
*
* \returns LIBUSB_ERROR_INVALID_PARAM if the option or arguments are invalid
* \returns LIBUSB_ERROR_NOT_SUPPORTED if the option is valid but not supported
* on this platform
+ * \returns LIBUSB_ERROR_NOT_FOUND if LIBUSB_OPTION_USE_USBDK is valid on this platform but UsbDk is not available
*/
int API_EXPORTED libusb_set_option(libusb_context *ctx,
enum libusb_option option, ...)
int arg, r = LIBUSB_SUCCESS;
va_list ap;
- USBI_GET_CONTEXT(ctx);
+ ctx = usbi_get_context(ctx);
va_start(ap, option);
switch (option) {
/* Handle all backend-specific options here */
case LIBUSB_OPTION_USE_USBDK:
+ case LIBUSB_OPTION_WEAK_AUTHORITY:
if (usbi_backend.set_option)
r = usbi_backend.set_option(ctx, option, ap);
else
usbi_mutex_static_lock(&default_context_lock);
- if (!timestamp_origin.tv_sec) {
- usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, ×tamp_origin);
- }
+ if (!timestamp_origin.tv_sec)
+ usbi_get_monotonic_time(×tamp_origin);
if (!context && usbi_default_context) {
usbi_dbg("reusing default context");
return 0;
}
- ctx = calloc(1, sizeof(*ctx) + priv_size);
+ ctx = calloc(1, PTR_ALIGN(sizeof(*ctx)) + priv_size);
if (!ctx) {
r = LIBUSB_ERROR_NO_MEM;
goto err_unlock;
usbi_mutex_static_lock(&active_contexts_lock);
if (first_init) {
first_init = 0;
- list_init (&active_contexts_list);
+ list_init(&active_contexts_list);
}
list_add (&ctx->list, &active_contexts_list);
usbi_mutex_static_unlock(&active_contexts_lock);
}
usbi_mutex_static_lock(&active_contexts_lock);
- list_del (&ctx->list);
+ list_del(&ctx->list);
usbi_mutex_static_unlock(&active_contexts_lock);
usbi_mutex_lock(&ctx->usb_devs_lock);
- list_for_each_entry_safe(dev, next, &ctx->usb_devs, list, struct libusb_device) {
+ for_each_device_safe(ctx, dev, next) {
list_del(&dev->list);
libusb_unref_device(dev);
}
* before your application terminates.
* \param ctx the context to deinitialize, or NULL for the default context
*/
-void API_EXPORTED libusb_exit(struct libusb_context *ctx)
+void API_EXPORTED libusb_exit(libusb_context *ctx)
{
struct libusb_device *dev, *next;
struct timeval tv = { 0, 0 };
+ int destroying_default_context = 0;
- usbi_dbg("");
- USBI_GET_CONTEXT(ctx);
+ usbi_dbg(" ");
+
+ ctx = usbi_get_context(ctx);
/* if working with default context, only actually do the deinitialization
* if we're the last user */
usbi_mutex_static_lock(&default_context_lock);
if (ctx == usbi_default_context) {
+ if (!usbi_default_context) {
+ usbi_dbg("no default context, not initialized?");
+ usbi_mutex_static_unlock(&default_context_lock);
+ return;
+ }
+
if (--default_context_refcnt > 0) {
usbi_dbg("not destroying default context");
usbi_mutex_static_unlock(&default_context_lock);
return;
}
usbi_dbg("destroying default context");
- usbi_default_context = NULL;
+
+ /*
+ * Setting this flag without unlocking the default context, as
+ * we are actually destroying the default context.
+ * usbi_default_context is not set to NULL yet, as all activities
+ * would only stop after usbi_backend->exit() returns.
+ */
+ destroying_default_context = 1;
+ } else {
+ /* Unlock default context, as we're not modifying it. */
+ usbi_mutex_static_unlock(&default_context_lock);
}
- usbi_mutex_static_unlock(&default_context_lock);
usbi_mutex_static_lock(&active_contexts_lock);
- list_del (&ctx->list);
+ list_del(&ctx->list);
usbi_mutex_static_unlock(&active_contexts_lock);
if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
libusb_handle_events_timeout(ctx, &tv);
usbi_mutex_lock(&ctx->usb_devs_lock);
- list_for_each_entry_safe(dev, next, &ctx->usb_devs, list, struct libusb_device) {
+ for_each_device_safe(ctx, dev, next) {
list_del(&dev->list);
libusb_unref_device(dev);
}
usbi_mutex_destroy(&ctx->usb_devs_lock);
usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
free(ctx);
+
+ if (destroying_default_context) {
+ usbi_default_context = NULL;
+ usbi_mutex_static_unlock(&default_context_lock);
+ }
}
/** \ingroup libusb_misc
#ifdef LIBUSB_PRINTF_WIN32
/*
* Prior to VS2015, Microsoft did not provide the snprintf() function and
- * provided a vsnprintf() that did not guarantee NULL-terminated output.
+ * provided a vsnprintf() that did not guarantee NUL-terminated output.
* Microsoft did provide a _snprintf() function, but again it did not
* guarantee NULL-terminated output.
*
- * The below implementations guarantee NULL-terminated output and are
+ * The below implementations guarantee NUL-terminated output and are
* C99 compliant.
*/
int usbi_snprintf(char *str, size_t size, const char *format, ...)
{
- va_list ap;
+ va_list args;
int ret;
- va_start(ap, format);
- ret = usbi_vsnprintf(str, size, format, ap);
- va_end(ap);
+ va_start(args, format);
+ ret = usbi_vsnprintf(str, size, format, args);
+ va_end(args);
return ret;
}
-int usbi_vsnprintf(char *str, size_t size, const char *format, va_list ap)
+int usbi_vsnprintf(char *str, size_t size, const char *format, va_list args)
{
int ret;
- ret = _vsnprintf(str, size, format, ap);
+ ret = _vsnprintf(str, size, format, args);
if (ret < 0 || ret == (int)size) {
- /* Output is truncated, ensure buffer is NULL-terminated and
+ /* Output is truncated, ensure buffer is NUL-terminated and
* determine how many characters would have been written. */
str[size - 1] = '\0';
if (ret < 0)
- ret = _vsnprintf(NULL, 0, format, ap);
+ ret = _vsnprintf(NULL, 0, format, args);
}
return ret;
}
#endif /* LIBUSB_PRINTF_WIN32 */
-static void usbi_log_str(enum libusb_log_level level, const char *str)
+static void log_str(enum libusb_log_level level, const char *str)
{
#if defined(USE_SYSTEM_LOGGING_FACILITY)
-#if defined(OS_WINDOWS)
- OutputDebugString(str);
-#elif defined(OS_WINCE)
- /* Windows CE only supports the Unicode version of OutputDebugString. */
- WCHAR wbuf[USBI_MAX_LOG_LEN];
- MultiByteToWideChar(CP_UTF8, 0, str, -1, wbuf, sizeof(wbuf));
- OutputDebugStringW(wbuf);
-#elif defined(__ANDROID__)
- int priority = ANDROID_LOG_UNKNOWN;
+#if defined(__ANDROID__)
+ int priority;
switch (level) {
- case LIBUSB_LOG_LEVEL_NONE: return;
+ case LIBUSB_LOG_LEVEL_NONE: return; /* Impossible, but keeps compiler happy */
case LIBUSB_LOG_LEVEL_ERROR: priority = ANDROID_LOG_ERROR; break;
case LIBUSB_LOG_LEVEL_WARNING: priority = ANDROID_LOG_WARN; break;
case LIBUSB_LOG_LEVEL_INFO: priority = ANDROID_LOG_INFO; break;
case LIBUSB_LOG_LEVEL_DEBUG: priority = ANDROID_LOG_DEBUG; break;
+ default: priority = ANDROID_LOG_UNKNOWN;
}
__android_log_write(priority, "libusb", str);
-#elif defined(HAVE_SYSLOG_FUNC)
- int syslog_level = LOG_INFO;
+#elif defined(_WIN32)
+ UNUSED(level);
+ OutputDebugStringA(str);
+#elif defined(HAVE_SYSLOG)
+ int syslog_level;
switch (level) {
- case LIBUSB_LOG_LEVEL_NONE: return;
+ case LIBUSB_LOG_LEVEL_NONE: return; /* Impossible, but keeps compiler happy */
case LIBUSB_LOG_LEVEL_ERROR: syslog_level = LOG_ERR; break;
case LIBUSB_LOG_LEVEL_WARNING: syslog_level = LOG_WARNING; break;
case LIBUSB_LOG_LEVEL_INFO: syslog_level = LOG_INFO; break;
case LIBUSB_LOG_LEVEL_DEBUG: syslog_level = LOG_DEBUG; break;
+ default: syslog_level = LOG_INFO;
}
syslog(syslog_level, "%s", str);
-#else /* All of gcc, Clang, XCode seem to use #warning */
+#else /* All of gcc, Clang, Xcode seem to use #warning */
#warning System logging is not supported on this platform. Logging to stderr will be used instead.
+ UNUSED(level);
fputs(str, stderr);
#endif
#else
- fputs(str, stderr);
+ /* Global log handler */
+ if (log_handler)
+ log_handler(NULL, level, str);
+ else
+ fputs(str, stderr);
#endif /* USE_SYSTEM_LOGGING_FACILITY */
- UNUSED(level);
}
-void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
+static void log_v(struct libusb_context *ctx, enum libusb_log_level level,
const char *function, const char *format, va_list args)
{
const char *prefix;
char buf[USBI_MAX_LOG_LEN];
- struct timespec now;
int global_debug, header_len, text_len;
static int has_debug_header_been_displayed = 0;
global_debug = 1;
UNUSED(ctx);
#else
- enum libusb_log_level ctx_level = LIBUSB_LOG_LEVEL_NONE;
+ enum libusb_log_level ctx_level;
- USBI_GET_CONTEXT(ctx);
+ ctx = usbi_get_context(ctx);
if (ctx)
ctx_level = ctx->debug;
else
ctx_level = get_env_debug_level();
- if (ctx_level == LIBUSB_LOG_LEVEL_NONE)
- return;
- if (level == LIBUSB_LOG_LEVEL_WARNING && ctx_level < LIBUSB_LOG_LEVEL_WARNING)
- return;
- if (level == LIBUSB_LOG_LEVEL_INFO && ctx_level < LIBUSB_LOG_LEVEL_INFO)
- return;
- if (level == LIBUSB_LOG_LEVEL_DEBUG && ctx_level < LIBUSB_LOG_LEVEL_DEBUG)
+ if (ctx_level < level)
return;
global_debug = (ctx_level == LIBUSB_LOG_LEVEL_DEBUG);
#endif
- usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, &now);
- if ((global_debug) && (!has_debug_header_been_displayed)) {
- has_debug_header_been_displayed = 1;
- usbi_log_str(LIBUSB_LOG_LEVEL_DEBUG, "[timestamp] [threadID] facility level [function call] <message>" USBI_LOG_LINE_END);
- usbi_log_str(LIBUSB_LOG_LEVEL_DEBUG, "--------------------------------------------------------------------------------" USBI_LOG_LINE_END);
- }
- if (now.tv_nsec < timestamp_origin.tv_nsec) {
- now.tv_sec--;
- now.tv_nsec += 1000000000L;
- }
- now.tv_sec -= timestamp_origin.tv_sec;
- now.tv_nsec -= timestamp_origin.tv_nsec;
-
switch (level) {
- case LIBUSB_LOG_LEVEL_NONE:
+ case LIBUSB_LOG_LEVEL_NONE: /* Impossible, but keeps compiler happy */
return;
case LIBUSB_LOG_LEVEL_ERROR:
prefix = "error";
}
if (global_debug) {
+ struct timespec timestamp;
+
+ if (!has_debug_header_been_displayed) {
+ has_debug_header_been_displayed = 1;
+ log_str(LIBUSB_LOG_LEVEL_DEBUG, "[timestamp] [threadID] facility level [function call] <message>" USBI_LOG_LINE_END);
+ log_str(LIBUSB_LOG_LEVEL_DEBUG, "--------------------------------------------------------------------------------" USBI_LOG_LINE_END);
+ }
+
+ usbi_get_monotonic_time(×tamp);
+ TIMESPEC_SUB(×tamp, ×tamp_origin, ×tamp);
+
header_len = snprintf(buf, sizeof(buf),
- "[%2d.%06d] [%08x] libusb: %s [%s] ",
- (int)now.tv_sec, (int)(now.tv_nsec / 1000L), usbi_get_tid(), prefix, function);
+ "[%2ld.%06ld] [%08x] libusb: %s [%s] ",
+ (long)timestamp.tv_sec, (long)(timestamp.tv_nsec / 1000L), usbi_get_tid(), prefix, function);
} else {
header_len = snprintf(buf, sizeof(buf),
"libusb: %s [%s] ", prefix, function);
}
if (header_len < 0 || header_len >= (int)sizeof(buf)) {
- /* Somehow snprintf failed to write to the buffer,
+ /* Somehow snprintf() failed to write to the buffer,
* remove the header so something useful is output. */
header_len = 0;
}
- /* Make sure buffer is NUL terminated */
- buf[header_len] = '\0';
- text_len = vsnprintf(buf + header_len, sizeof(buf) - header_len,
+
+ text_len = vsnprintf(buf + header_len, sizeof(buf) - (size_t)header_len,
format, args);
if (text_len < 0 || text_len + header_len >= (int)sizeof(buf)) {
/* Truncated log output. On some platforms a -1 return value means
* that the output was truncated. */
- text_len = sizeof(buf) - header_len;
+ text_len = (int)sizeof(buf) - header_len;
}
- if (header_len + text_len + sizeof(USBI_LOG_LINE_END) >= sizeof(buf)) {
+ if (header_len + text_len + (int)sizeof(USBI_LOG_LINE_END) >= (int)sizeof(buf)) {
/* Need to truncate the text slightly to fit on the terminator. */
- text_len -= (header_len + text_len + sizeof(USBI_LOG_LINE_END)) - sizeof(buf);
+ text_len -= (header_len + text_len + (int)sizeof(USBI_LOG_LINE_END)) - (int)sizeof(buf);
}
strcpy(buf + header_len + text_len, USBI_LOG_LINE_END);
- usbi_log_str(level, buf);
+ log_str(level, buf);
+
+ /* Per-context log handler */
+#ifndef ENABLE_DEBUG_LOGGING
+ if (ctx && ctx->log_handler)
+ ctx->log_handler(ctx, level, buf);
+#endif
}
void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
{
va_list args;
- va_start (args, format);
- usbi_log_v(ctx, level, function, format, args);
- va_end (args);
+ va_start(args, format);
+ log_v(ctx, level, function, format, args);
+ va_end(args);
}
#endif /* ENABLE_LOGGING */
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <config.h>
+#include "libusbi.h"
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
#include <string.h>
-#include "libusbi.h"
-
-#define DESC_HEADER_LENGTH 2
-#define DEVICE_DESC_LENGTH 18
-#define CONFIG_DESC_LENGTH 9
-#define INTERFACE_DESC_LENGTH 9
-#define ENDPOINT_DESC_LENGTH 7
-#define ENDPOINT_AUDIO_DESC_LENGTH 9
+#define DESC_HEADER_LENGTH 2
/** @defgroup libusb_desc USB descriptors
* This page details how to examine the various standard USB descriptors
* for detected devices
*/
-/* set host_endian if the w values are already in host endian format,
- * as opposed to bus endian. */
-int usbi_parse_descriptor(const unsigned char *source, const char *descriptor,
- void *dest, int host_endian)
+#define READ_LE16(p) ((uint16_t) \
+ (((uint16_t)((p)[1]) << 8) | \
+ ((uint16_t)((p)[0]))))
+
+#define READ_LE32(p) ((uint32_t) \
+ (((uint32_t)((p)[3]) << 24) | \
+ ((uint32_t)((p)[2]) << 16) | \
+ ((uint32_t)((p)[1]) << 8) | \
+ ((uint32_t)((p)[0]))))
+
+static void parse_descriptor(const void *source, const char *descriptor, void *dest)
{
- const unsigned char *sp = source;
- unsigned char *dp = dest;
- uint16_t w;
- const char *cp;
- uint32_t d;
-
- for (cp = descriptor; *cp; cp++) {
- switch (*cp) {
- case 'b': /* 8-bit byte */
- *dp++ = *sp++;
- break;
- case 'w': /* 16-bit word, convert from little endian to CPU */
- dp += ((uintptr_t)dp & 1); /* Align to word boundary */
-
- if (host_endian) {
- memcpy(dp, sp, 2);
- } else {
- w = (sp[1] << 8) | sp[0];
- *((uint16_t *)dp) = w;
- }
- sp += 2;
- dp += 2;
- break;
- case 'd': /* 32-bit word, convert from little endian to CPU */
- dp += ((uintptr_t)dp & 1); /* Align to word boundary */
-
- if (host_endian) {
- memcpy(dp, sp, 4);
- } else {
- d = (sp[3] << 24) | (sp[2] << 16) |
- (sp[1] << 8) | sp[0];
- *((uint32_t *)dp) = d;
- }
- sp += 4;
- dp += 4;
- break;
- case 'u': /* 16 byte UUID */
- memcpy(dp, sp, 16);
- sp += 16;
- dp += 16;
- break;
+ const uint8_t *sp = source;
+ uint8_t *dp = dest;
+ char field_type;
+
+ while (*descriptor) {
+ field_type = *descriptor++;
+ switch (field_type) {
+ case 'b': /* 8-bit byte */
+ *dp++ = *sp++;
+ break;
+ case 'w': /* 16-bit word, convert from little endian to CPU */
+ dp += ((uintptr_t)dp & 1); /* Align to 16-bit word boundary */
+
+ *((uint16_t *)dp) = READ_LE16(sp);
+ sp += 2;
+ dp += 2;
+ break;
+ case 'd': /* 32-bit word, convert from little endian to CPU */
+ dp += 4 - ((uintptr_t)dp & 3); /* Align to 32-bit word boundary */
+
+ *((uint32_t *)dp) = READ_LE32(sp);
+ sp += 4;
+ dp += 4;
+ break;
+ case 'u': /* 16 byte UUID */
+ memcpy(dp, sp, 16);
+ sp += 16;
+ dp += 16;
+ break;
}
}
-
- return (int) (sp - source);
}
static void clear_endpoint(struct libusb_endpoint_descriptor *endpoint)
{
- free((void *) endpoint->extra);
+ free((void *)endpoint->extra);
}
static int parse_endpoint(struct libusb_context *ctx,
- struct libusb_endpoint_descriptor *endpoint, unsigned char *buffer,
- int size, int host_endian)
+ struct libusb_endpoint_descriptor *endpoint, const uint8_t *buffer, int size)
{
- struct usb_descriptor_header header;
- unsigned char *extra;
- unsigned char *begin;
+ const struct usbi_descriptor_header *header;
+ const uint8_t *begin;
+ void *extra;
int parsed = 0;
int len;
return LIBUSB_ERROR_IO;
}
- usbi_parse_descriptor(buffer, "bb", &header, 0);
- if (header.bDescriptorType != LIBUSB_DT_ENDPOINT) {
- usbi_err(ctx, "unexpected descriptor %x (expected %x)",
- header.bDescriptorType, LIBUSB_DT_ENDPOINT);
+ header = (const struct usbi_descriptor_header *)buffer;
+ if (header->bDescriptorType != LIBUSB_DT_ENDPOINT) {
+ usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)",
+ header->bDescriptorType, LIBUSB_DT_ENDPOINT);
return parsed;
- }
- if (header.bLength > size) {
- usbi_warn(ctx, "short endpoint descriptor read %d/%d",
- size, header.bLength);
- return parsed;
- }
- if (header.bLength >= ENDPOINT_AUDIO_DESC_LENGTH)
- usbi_parse_descriptor(buffer, "bbbbwbbb", endpoint, host_endian);
- else if (header.bLength >= ENDPOINT_DESC_LENGTH)
- usbi_parse_descriptor(buffer, "bbbbwb", endpoint, host_endian);
- else {
- usbi_err(ctx, "invalid endpoint bLength (%d)", header.bLength);
+ } else if (header->bLength < LIBUSB_DT_ENDPOINT_SIZE) {
+ usbi_err(ctx, "invalid endpoint bLength (%u)", header->bLength);
return LIBUSB_ERROR_IO;
+ } else if (header->bLength > size) {
+ usbi_warn(ctx, "short endpoint descriptor read %d/%u",
+ size, header->bLength);
+ return parsed;
}
- buffer += header.bLength;
- size -= header.bLength;
- parsed += header.bLength;
+ if (header->bLength >= LIBUSB_DT_ENDPOINT_AUDIO_SIZE)
+ parse_descriptor(buffer, "bbbbwbbb", endpoint);
+ else
+ parse_descriptor(buffer, "bbbbwb", endpoint);
+
+ buffer += header->bLength;
+ size -= header->bLength;
+ parsed += header->bLength;
/* Skip over the rest of the Class Specific or Vendor Specific */
/* descriptors */
begin = buffer;
while (size >= DESC_HEADER_LENGTH) {
- usbi_parse_descriptor(buffer, "bb", &header, 0);
- if (header.bLength < DESC_HEADER_LENGTH) {
- usbi_err(ctx, "invalid extra ep desc len (%d)",
- header.bLength);
+ header = (const struct usbi_descriptor_header *)buffer;
+ if (header->bLength < DESC_HEADER_LENGTH) {
+ usbi_err(ctx, "invalid extra ep desc len (%u)",
+ header->bLength);
return LIBUSB_ERROR_IO;
- } else if (header.bLength > size) {
- usbi_warn(ctx, "short extra ep desc read %d/%d",
- size, header.bLength);
+ } else if (header->bLength > size) {
+ usbi_warn(ctx, "short extra ep desc read %d/%u",
+ size, header->bLength);
return parsed;
}
/* If we find another "proper" descriptor then we're done */
- if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
- (header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
- (header.bDescriptorType == LIBUSB_DT_CONFIG) ||
- (header.bDescriptorType == LIBUSB_DT_DEVICE))
+ if (header->bDescriptorType == LIBUSB_DT_ENDPOINT ||
+ header->bDescriptorType == LIBUSB_DT_INTERFACE ||
+ header->bDescriptorType == LIBUSB_DT_CONFIG ||
+ header->bDescriptorType == LIBUSB_DT_DEVICE)
break;
- usbi_dbg("skipping descriptor %x", header.bDescriptorType);
- buffer += header.bLength;
- size -= header.bLength;
- parsed += header.bLength;
+ usbi_dbg("skipping descriptor 0x%x", header->bDescriptorType);
+ buffer += header->bLength;
+ size -= header->bLength;
+ parsed += header->bLength;
}
/* Copy any unknown descriptors into a storage area for drivers */
/* to later parse */
len = (int)(buffer - begin);
- if (!len) {
- endpoint->extra = NULL;
- endpoint->extra_length = 0;
+ if (len <= 0)
return parsed;
- }
- extra = malloc(len);
- endpoint->extra = extra;
- if (!extra) {
- endpoint->extra_length = 0;
+ extra = malloc((size_t)len);
+ if (!extra)
return LIBUSB_ERROR_NO_MEM;
- }
memcpy(extra, begin, len);
+ endpoint->extra = extra;
endpoint->extra_length = len;
return parsed;
static void clear_interface(struct libusb_interface *usb_interface)
{
int i;
- int j;
if (usb_interface->altsetting) {
for (i = 0; i < usb_interface->num_altsetting; i++) {
struct libusb_interface_descriptor *ifp =
(struct libusb_interface_descriptor *)
usb_interface->altsetting + i;
- free((void *) ifp->extra);
+
+ free((void *)ifp->extra);
if (ifp->endpoint) {
+ uint8_t j;
+
for (j = 0; j < ifp->bNumEndpoints; j++)
clear_endpoint((struct libusb_endpoint_descriptor *)
ifp->endpoint + j);
}
- free((void *) ifp->endpoint);
+ free((void *)ifp->endpoint);
}
}
- free((void *) usb_interface->altsetting);
+ free((void *)usb_interface->altsetting);
usb_interface->altsetting = NULL;
}
static int parse_interface(libusb_context *ctx,
- struct libusb_interface *usb_interface, unsigned char *buffer, int size,
- int host_endian)
+ struct libusb_interface *usb_interface, const uint8_t *buffer, int size)
{
- int i;
int len;
int r;
int parsed = 0;
int interface_number = -1;
- struct usb_descriptor_header header;
+ const struct usbi_descriptor_header *header;
+ const struct usbi_interface_descriptor *if_desc;
struct libusb_interface_descriptor *ifp;
- unsigned char *begin;
+ const uint8_t *begin;
- usb_interface->num_altsetting = 0;
+ while (size >= LIBUSB_DT_INTERFACE_SIZE) {
+ struct libusb_interface_descriptor *altsetting;
- while (size >= INTERFACE_DESC_LENGTH) {
- struct libusb_interface_descriptor *altsetting =
- (struct libusb_interface_descriptor *) usb_interface->altsetting;
- altsetting = usbi_reallocf(altsetting,
- sizeof(struct libusb_interface_descriptor) *
- (usb_interface->num_altsetting + 1));
+ altsetting = realloc((void *)usb_interface->altsetting,
+ sizeof(*altsetting) * (size_t)(usb_interface->num_altsetting + 1));
if (!altsetting) {
r = LIBUSB_ERROR_NO_MEM;
goto err;
usb_interface->altsetting = altsetting;
ifp = altsetting + usb_interface->num_altsetting;
- usbi_parse_descriptor(buffer, "bbbbbbbbb", ifp, 0);
+ parse_descriptor(buffer, "bbbbbbbbb", ifp);
if (ifp->bDescriptorType != LIBUSB_DT_INTERFACE) {
- usbi_err(ctx, "unexpected descriptor %x (expected %x)",
+ usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)",
ifp->bDescriptorType, LIBUSB_DT_INTERFACE);
return parsed;
- }
- if (ifp->bLength < INTERFACE_DESC_LENGTH) {
- usbi_err(ctx, "invalid interface bLength (%d)",
+ } else if (ifp->bLength < LIBUSB_DT_INTERFACE_SIZE) {
+ usbi_err(ctx, "invalid interface bLength (%u)",
ifp->bLength);
r = LIBUSB_ERROR_IO;
goto err;
- }
- if (ifp->bLength > size) {
- usbi_warn(ctx, "short intf descriptor read %d/%d",
+ } else if (ifp->bLength > size) {
+ usbi_warn(ctx, "short intf descriptor read %d/%u",
size, ifp->bLength);
return parsed;
- }
- if (ifp->bNumEndpoints > USB_MAXENDPOINTS) {
- usbi_err(ctx, "too many endpoints (%d)", ifp->bNumEndpoints);
+ } else if (ifp->bNumEndpoints > USB_MAXENDPOINTS) {
+ usbi_err(ctx, "too many endpoints (%u)", ifp->bNumEndpoints);
r = LIBUSB_ERROR_IO;
goto err;
}
/* Skip over any interface, class or vendor descriptors */
while (size >= DESC_HEADER_LENGTH) {
- usbi_parse_descriptor(buffer, "bb", &header, 0);
- if (header.bLength < DESC_HEADER_LENGTH) {
+ header = (const struct usbi_descriptor_header *)buffer;
+ if (header->bLength < DESC_HEADER_LENGTH) {
usbi_err(ctx,
- "invalid extra intf desc len (%d)",
- header.bLength);
+ "invalid extra intf desc len (%u)",
+ header->bLength);
r = LIBUSB_ERROR_IO;
goto err;
- } else if (header.bLength > size) {
+ } else if (header->bLength > size) {
usbi_warn(ctx,
- "short extra intf desc read %d/%d",
- size, header.bLength);
+ "short extra intf desc read %d/%u",
+ size, header->bLength);
return parsed;
}
/* If we find another "proper" descriptor then we're done */
- if ((header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
- (header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
- (header.bDescriptorType == LIBUSB_DT_CONFIG) ||
- (header.bDescriptorType == LIBUSB_DT_DEVICE))
+ if (header->bDescriptorType == LIBUSB_DT_INTERFACE ||
+ header->bDescriptorType == LIBUSB_DT_ENDPOINT ||
+ header->bDescriptorType == LIBUSB_DT_CONFIG ||
+ header->bDescriptorType == LIBUSB_DT_DEVICE)
break;
- buffer += header.bLength;
- parsed += header.bLength;
- size -= header.bLength;
+ buffer += header->bLength;
+ parsed += header->bLength;
+ size -= header->bLength;
}
/* Copy any unknown descriptors into a storage area for */
/* drivers to later parse */
len = (int)(buffer - begin);
- if (len) {
- ifp->extra = malloc(len);
- if (!ifp->extra) {
+ if (len > 0) {
+ void *extra = malloc((size_t)len);
+
+ if (!extra) {
r = LIBUSB_ERROR_NO_MEM;
goto err;
}
- memcpy((unsigned char *) ifp->extra, begin, len);
+
+ memcpy(extra, begin, len);
+ ifp->extra = extra;
ifp->extra_length = len;
}
if (ifp->bNumEndpoints > 0) {
struct libusb_endpoint_descriptor *endpoint;
- endpoint = calloc(ifp->bNumEndpoints, sizeof(struct libusb_endpoint_descriptor));
- ifp->endpoint = endpoint;
+ uint8_t i;
+
+ endpoint = calloc(ifp->bNumEndpoints, sizeof(*endpoint));
if (!endpoint) {
r = LIBUSB_ERROR_NO_MEM;
goto err;
}
+ ifp->endpoint = endpoint;
for (i = 0; i < ifp->bNumEndpoints; i++) {
- r = parse_endpoint(ctx, endpoint + i, buffer, size,
- host_endian);
+ r = parse_endpoint(ctx, endpoint + i, buffer, size);
if (r < 0)
goto err;
if (r == 0) {
- ifp->bNumEndpoints = (uint8_t)i;
+ ifp->bNumEndpoints = i;
break;
}
}
/* We check to see if it's an alternate to this one */
- ifp = (struct libusb_interface_descriptor *) buffer;
+ if_desc = (const struct usbi_interface_descriptor *)buffer;
if (size < LIBUSB_DT_INTERFACE_SIZE ||
- ifp->bDescriptorType != LIBUSB_DT_INTERFACE ||
- ifp->bInterfaceNumber != interface_number)
+ if_desc->bDescriptorType != LIBUSB_DT_INTERFACE ||
+ if_desc->bInterfaceNumber != interface_number)
return parsed;
}
static void clear_configuration(struct libusb_config_descriptor *config)
{
- int i;
+ uint8_t i;
+
if (config->interface) {
for (i = 0; i < config->bNumInterfaces; i++)
clear_interface((struct libusb_interface *)
config->interface + i);
}
- free((void *) config->interface);
- free((void *) config->extra);
+ free((void *)config->interface);
+ free((void *)config->extra);
}
static int parse_configuration(struct libusb_context *ctx,
- struct libusb_config_descriptor *config, unsigned char *buffer,
- int size, int host_endian)
+ struct libusb_config_descriptor *config, const uint8_t *buffer, int size)
{
- int i;
+ uint8_t i;
int r;
- struct usb_descriptor_header header;
+ const struct usbi_descriptor_header *header;
struct libusb_interface *usb_interface;
if (size < LIBUSB_DT_CONFIG_SIZE) {
return LIBUSB_ERROR_IO;
}
- usbi_parse_descriptor(buffer, "bbwbbbbb", config, host_endian);
+ parse_descriptor(buffer, "bbwbbbbb", config);
if (config->bDescriptorType != LIBUSB_DT_CONFIG) {
- usbi_err(ctx, "unexpected descriptor %x (expected %x)",
+ usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)",
config->bDescriptorType, LIBUSB_DT_CONFIG);
return LIBUSB_ERROR_IO;
- }
- if (config->bLength < LIBUSB_DT_CONFIG_SIZE) {
- usbi_err(ctx, "invalid config bLength (%d)", config->bLength);
+ } else if (config->bLength < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(ctx, "invalid config bLength (%u)", config->bLength);
return LIBUSB_ERROR_IO;
- }
- if (config->bLength > size) {
- usbi_err(ctx, "short config descriptor read %d/%d",
+ } else if (config->bLength > size) {
+ usbi_err(ctx, "short config descriptor read %d/%u",
size, config->bLength);
return LIBUSB_ERROR_IO;
- }
- if (config->bNumInterfaces > USB_MAXINTERFACES) {
- usbi_err(ctx, "too many interfaces (%d)", config->bNumInterfaces);
+ } else if (config->bNumInterfaces > USB_MAXINTERFACES) {
+ usbi_err(ctx, "too many interfaces (%u)", config->bNumInterfaces);
return LIBUSB_ERROR_IO;
}
- usb_interface = calloc(config->bNumInterfaces, sizeof(struct libusb_interface));
- config->interface = usb_interface;
+ usb_interface = calloc(config->bNumInterfaces, sizeof(*usb_interface));
if (!usb_interface)
return LIBUSB_ERROR_NO_MEM;
+ config->interface = usb_interface;
+
buffer += config->bLength;
size -= config->bLength;
- config->extra = NULL;
- config->extra_length = 0;
-
for (i = 0; i < config->bNumInterfaces; i++) {
int len;
- unsigned char *begin;
+ const uint8_t *begin;
/* Skip over the rest of the Class Specific or Vendor */
/* Specific descriptors */
begin = buffer;
while (size >= DESC_HEADER_LENGTH) {
- usbi_parse_descriptor(buffer, "bb", &header, 0);
-
- if (header.bLength < DESC_HEADER_LENGTH) {
+ header = (const struct usbi_descriptor_header *)buffer;
+ if (header->bLength < DESC_HEADER_LENGTH) {
usbi_err(ctx,
- "invalid extra config desc len (%d)",
- header.bLength);
+ "invalid extra config desc len (%u)",
+ header->bLength);
r = LIBUSB_ERROR_IO;
goto err;
- } else if (header.bLength > size) {
+ } else if (header->bLength > size) {
usbi_warn(ctx,
- "short extra config desc read %d/%d",
- size, header.bLength);
- config->bNumInterfaces = (uint8_t)i;
+ "short extra config desc read %d/%u",
+ size, header->bLength);
+ config->bNumInterfaces = i;
return size;
}
/* If we find another "proper" descriptor then we're done */
- if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
- (header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
- (header.bDescriptorType == LIBUSB_DT_CONFIG) ||
- (header.bDescriptorType == LIBUSB_DT_DEVICE))
+ if (header->bDescriptorType == LIBUSB_DT_ENDPOINT ||
+ header->bDescriptorType == LIBUSB_DT_INTERFACE ||
+ header->bDescriptorType == LIBUSB_DT_CONFIG ||
+ header->bDescriptorType == LIBUSB_DT_DEVICE)
break;
- usbi_dbg("skipping descriptor 0x%x", header.bDescriptorType);
- buffer += header.bLength;
- size -= header.bLength;
+ usbi_dbg("skipping descriptor 0x%x", header->bDescriptorType);
+ buffer += header->bLength;
+ size -= header->bLength;
}
/* Copy any unknown descriptors into a storage area for */
/* drivers to later parse */
len = (int)(buffer - begin);
- if (len) {
- /* FIXME: We should realloc and append here */
- if (!config->extra_length) {
- config->extra = malloc(len);
- if (!config->extra) {
- r = LIBUSB_ERROR_NO_MEM;
- goto err;
- }
+ if (len > 0) {
+ uint8_t *extra = realloc((void *)config->extra,
+ (size_t)(config->extra_length + len));
- memcpy((unsigned char *) config->extra, begin, len);
- config->extra_length = len;
+ if (!extra) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err;
}
+
+ memcpy(extra + config->extra_length, begin, len);
+ config->extra = extra;
+ config->extra_length += len;
}
- r = parse_interface(ctx, usb_interface + i, buffer, size, host_endian);
+ r = parse_interface(ctx, usb_interface + i, buffer, size);
if (r < 0)
goto err;
if (r == 0) {
- config->bNumInterfaces = (uint8_t)i;
+ config->bNumInterfaces = i;
break;
}
}
static int raw_desc_to_config(struct libusb_context *ctx,
- unsigned char *buf, int size, int host_endian,
- struct libusb_config_descriptor **config)
+ const uint8_t *buf, int size, struct libusb_config_descriptor **config)
{
- struct libusb_config_descriptor *_config = malloc(sizeof(*_config));
+ struct libusb_config_descriptor *_config = calloc(1, sizeof(*_config));
int r;
-
+
if (!_config)
return LIBUSB_ERROR_NO_MEM;
- r = parse_configuration(ctx, _config, buf, size, host_endian);
+ r = parse_configuration(ctx, _config, buf, size);
if (r < 0) {
usbi_err(ctx, "parse_configuration failed with error %d", r);
free(_config);
} else if (r > 0) {
usbi_warn(ctx, "still %d bytes of descriptor data left", r);
}
-
+
*config = _config;
return LIBUSB_SUCCESS;
}
-int usbi_device_cache_descriptor(libusb_device *dev)
+static int get_active_config_descriptor(struct libusb_device *dev,
+ uint8_t *buffer, size_t size)
{
- int r, host_endian = 0;
+ int r = usbi_backend.get_active_config_descriptor(dev, buffer, size);
- r = usbi_backend.get_device_descriptor(dev, (unsigned char *) &dev->device_descriptor,
- &host_endian);
if (r < 0)
return r;
- if (!host_endian) {
- dev->device_descriptor.bcdUSB = libusb_le16_to_cpu(dev->device_descriptor.bcdUSB);
- dev->device_descriptor.idVendor = libusb_le16_to_cpu(dev->device_descriptor.idVendor);
- dev->device_descriptor.idProduct = libusb_le16_to_cpu(dev->device_descriptor.idProduct);
- dev->device_descriptor.bcdDevice = libusb_le16_to_cpu(dev->device_descriptor.bcdDevice);
+ if (r < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(DEVICE_CTX(dev), "short config descriptor read %d/%d",
+ r, LIBUSB_DT_CONFIG_SIZE);
+ return LIBUSB_ERROR_IO;
+ } else if (r != (int)size) {
+ usbi_warn(DEVICE_CTX(dev), "short config descriptor read %d/%d",
+ r, (int)size);
}
- return LIBUSB_SUCCESS;
+ return r;
+}
+
+static int get_config_descriptor(struct libusb_device *dev, uint8_t config_idx,
+ uint8_t *buffer, size_t size)
+{
+ int r = usbi_backend.get_config_descriptor(dev, config_idx, buffer, size);
+
+ if (r < 0)
+ return r;
+ if (r < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(DEVICE_CTX(dev), "short config descriptor read %d/%d",
+ r, LIBUSB_DT_CONFIG_SIZE);
+ return LIBUSB_ERROR_IO;
+ } else if (r != (int)size) {
+ usbi_warn(DEVICE_CTX(dev), "short config descriptor read %d/%d",
+ r, (int)size);
+ }
+
+ return r;
}
/** \ingroup libusb_desc
int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev,
struct libusb_device_descriptor *desc)
{
- usbi_dbg("");
- memcpy((unsigned char *) desc, (unsigned char *) &dev->device_descriptor,
- sizeof (dev->device_descriptor));
+ usbi_dbg(" ");
+ static_assert(sizeof(dev->device_descriptor) == LIBUSB_DT_DEVICE_SIZE,
+ "struct libusb_device_descriptor is not expected size");
+ *desc = dev->device_descriptor;
return 0;
}
int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev,
struct libusb_config_descriptor **config)
{
- struct libusb_config_descriptor _config;
- unsigned char tmp[LIBUSB_DT_CONFIG_SIZE];
- unsigned char *buf = NULL;
- int host_endian = 0;
+ union usbi_config_desc_buf _config;
+ uint16_t config_len;
+ uint8_t *buf;
int r;
- r = usbi_backend.get_active_config_descriptor(dev, tmp,
- LIBUSB_DT_CONFIG_SIZE, &host_endian);
+ r = get_active_config_descriptor(dev, _config.buf, sizeof(_config.buf));
if (r < 0)
return r;
- if (r < LIBUSB_DT_CONFIG_SIZE) {
- usbi_err(dev->ctx, "short config descriptor read %d/%d",
- r, LIBUSB_DT_CONFIG_SIZE);
- return LIBUSB_ERROR_IO;
- }
- usbi_parse_descriptor(tmp, "bbw", &_config, host_endian);
- buf = malloc(_config.wTotalLength);
+ config_len = libusb_le16_to_cpu(_config.desc.wTotalLength);
+ buf = malloc(config_len);
if (!buf)
return LIBUSB_ERROR_NO_MEM;
- r = usbi_backend.get_active_config_descriptor(dev, buf,
- _config.wTotalLength, &host_endian);
+ r = get_active_config_descriptor(dev, buf, config_len);
if (r >= 0)
- r = raw_desc_to_config(dev->ctx, buf, r, host_endian, config);
+ r = raw_desc_to_config(DEVICE_CTX(dev), buf, r, config);
free(buf);
return r;
int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev,
uint8_t config_index, struct libusb_config_descriptor **config)
{
- struct libusb_config_descriptor _config;
- unsigned char tmp[LIBUSB_DT_CONFIG_SIZE];
- unsigned char *buf = NULL;
- int host_endian = 0;
+ union usbi_config_desc_buf _config;
+ uint16_t config_len;
+ uint8_t *buf;
int r;
- usbi_dbg("index %d", config_index);
- if (config_index >= dev->num_configurations)
+ usbi_dbg("index %u", config_index);
+ if (config_index >= dev->device_descriptor.bNumConfigurations)
return LIBUSB_ERROR_NOT_FOUND;
- r = usbi_backend.get_config_descriptor(dev, config_index, tmp,
- LIBUSB_DT_CONFIG_SIZE, &host_endian);
+ r = get_config_descriptor(dev, config_index, _config.buf, sizeof(_config.buf));
if (r < 0)
return r;
- if (r < LIBUSB_DT_CONFIG_SIZE) {
- usbi_err(dev->ctx, "short config descriptor read %d/%d",
- r, LIBUSB_DT_CONFIG_SIZE);
- return LIBUSB_ERROR_IO;
- }
- usbi_parse_descriptor(tmp, "bbw", &_config, host_endian);
- buf = malloc(_config.wTotalLength);
+ config_len = libusb_le16_to_cpu(_config.desc.wTotalLength);
+ buf = malloc(config_len);
if (!buf)
return LIBUSB_ERROR_NO_MEM;
- r = usbi_backend.get_config_descriptor(dev, config_index, buf,
- _config.wTotalLength, &host_endian);
+ r = get_config_descriptor(dev, config_index, buf, config_len);
if (r >= 0)
- r = raw_desc_to_config(dev->ctx, buf, r, host_endian, config);
+ r = raw_desc_to_config(DEVICE_CTX(dev), buf, r, config);
free(buf);
return r;
}
-/* iterate through all configurations, returning the index of the configuration
- * matching a specific bConfigurationValue in the idx output parameter, or -1
- * if the config was not found.
- * returns 0 on success or a LIBUSB_ERROR code
- */
-int usbi_get_config_index_by_value(struct libusb_device *dev,
- uint8_t bConfigurationValue, int *idx)
-{
- uint8_t i;
-
- usbi_dbg("value %d", bConfigurationValue);
- for (i = 0; i < dev->num_configurations; i++) {
- unsigned char tmp[6];
- int host_endian;
- int r = usbi_backend.get_config_descriptor(dev, i, tmp, sizeof(tmp),
- &host_endian);
- if (r < 0) {
- *idx = -1;
- return r;
- }
- if (tmp[5] == bConfigurationValue) {
- *idx = i;
- return 0;
- }
- }
-
- *idx = -1;
- return 0;
-}
-
/** \ingroup libusb_desc
* Get a USB configuration descriptor with a specific bConfigurationValue.
* This is a non-blocking function which does not involve any requests being
int API_EXPORTED libusb_get_config_descriptor_by_value(libusb_device *dev,
uint8_t bConfigurationValue, struct libusb_config_descriptor **config)
{
- int r, idx, host_endian;
- unsigned char *buf = NULL;
+ uint8_t idx;
+ int r;
if (usbi_backend.get_config_descriptor_by_value) {
+ void *buf;
+
r = usbi_backend.get_config_descriptor_by_value(dev,
- bConfigurationValue, &buf, &host_endian);
+ bConfigurationValue, &buf);
if (r < 0)
return r;
- return raw_desc_to_config(dev->ctx, buf, r, host_endian, config);
+
+ return raw_desc_to_config(DEVICE_CTX(dev), buf, r, config);
}
- r = usbi_get_config_index_by_value(dev, bConfigurationValue, &idx);
- if (r < 0)
- return r;
- else if (idx == -1)
- return LIBUSB_ERROR_NOT_FOUND;
- else
- return libusb_get_config_descriptor(dev, (uint8_t) idx, config);
+ usbi_dbg("value %u", bConfigurationValue);
+ for (idx = 0; idx < dev->device_descriptor.bNumConfigurations; idx++) {
+ union usbi_config_desc_buf _config;
+
+ r = get_config_descriptor(dev, idx, _config.buf, sizeof(_config.buf));
+ if (r < 0)
+ return r;
+
+ if (_config.desc.bConfigurationValue == bConfigurationValue)
+ return libusb_get_config_descriptor(dev, idx, config);
+ }
+
+ return LIBUSB_ERROR_NOT_FOUND;
}
/** \ingroup libusb_desc
* \returns another LIBUSB_ERROR code on error
*/
int API_EXPORTED libusb_get_ss_endpoint_companion_descriptor(
- struct libusb_context *ctx,
+ libusb_context *ctx,
const struct libusb_endpoint_descriptor *endpoint,
struct libusb_ss_endpoint_companion_descriptor **ep_comp)
{
- struct usb_descriptor_header header;
+ struct usbi_descriptor_header *header;
+ const uint8_t *buffer = endpoint->extra;
int size = endpoint->extra_length;
- const unsigned char *buffer = endpoint->extra;
*ep_comp = NULL;
while (size >= DESC_HEADER_LENGTH) {
- usbi_parse_descriptor(buffer, "bb", &header, 0);
- if (header.bLength < 2 || header.bLength > size) {
- usbi_err(ctx, "invalid descriptor length %d",
- header.bLength);
- return LIBUSB_ERROR_IO;
- }
- if (header.bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) {
- buffer += header.bLength;
- size -= header.bLength;
+ header = (struct usbi_descriptor_header *)buffer;
+ if (header->bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) {
+ if (header->bLength < DESC_HEADER_LENGTH) {
+ usbi_err(ctx, "invalid descriptor length %u",
+ header->bLength);
+ return LIBUSB_ERROR_IO;
+ }
+ buffer += header->bLength;
+ size -= header->bLength;
continue;
- }
- if (header.bLength < LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) {
- usbi_err(ctx, "invalid ss-ep-comp-desc length %d",
- header.bLength);
+ } else if (header->bLength < LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) {
+ usbi_err(ctx, "invalid ss-ep-comp-desc length %u",
+ header->bLength);
+ return LIBUSB_ERROR_IO;
+ } else if (header->bLength > size) {
+ usbi_err(ctx, "short ss-ep-comp-desc read %d/%u",
+ size, header->bLength);
return LIBUSB_ERROR_IO;
}
+
*ep_comp = malloc(sizeof(**ep_comp));
- if (*ep_comp == NULL)
+ if (!*ep_comp)
return LIBUSB_ERROR_NO_MEM;
- usbi_parse_descriptor(buffer, "bbbbw", *ep_comp, 0);
+ parse_descriptor(buffer, "bbbbw", *ep_comp);
return LIBUSB_SUCCESS;
}
return LIBUSB_ERROR_NOT_FOUND;
static int parse_bos(struct libusb_context *ctx,
struct libusb_bos_descriptor **bos,
- unsigned char *buffer, int size, int host_endian)
+ const uint8_t *buffer, int size)
{
- struct libusb_bos_descriptor bos_header, *_bos;
- struct libusb_bos_dev_capability_descriptor dev_cap;
- int i;
+ struct libusb_bos_descriptor *_bos;
+ const struct usbi_bos_descriptor *bos_desc;
+ const struct usbi_descriptor_header *header;
+ uint8_t i;
if (size < LIBUSB_DT_BOS_SIZE) {
usbi_err(ctx, "short bos descriptor read %d/%d",
return LIBUSB_ERROR_IO;
}
- usbi_parse_descriptor(buffer, "bbwb", &bos_header, host_endian);
- if (bos_header.bDescriptorType != LIBUSB_DT_BOS) {
- usbi_err(ctx, "unexpected descriptor %x (expected %x)",
- bos_header.bDescriptorType, LIBUSB_DT_BOS);
+ bos_desc = (const struct usbi_bos_descriptor *)buffer;
+ if (bos_desc->bDescriptorType != LIBUSB_DT_BOS) {
+ usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)",
+ bos_desc->bDescriptorType, LIBUSB_DT_BOS);
return LIBUSB_ERROR_IO;
- }
- if (bos_header.bLength < LIBUSB_DT_BOS_SIZE) {
- usbi_err(ctx, "invalid bos bLength (%d)", bos_header.bLength);
+ } else if (bos_desc->bLength < LIBUSB_DT_BOS_SIZE) {
+ usbi_err(ctx, "invalid bos bLength (%u)", bos_desc->bLength);
return LIBUSB_ERROR_IO;
- }
- if (bos_header.bLength > size) {
- usbi_err(ctx, "short bos descriptor read %d/%d",
- size, bos_header.bLength);
+ } else if (bos_desc->bLength > size) {
+ usbi_err(ctx, "short bos descriptor read %d/%u",
+ size, bos_desc->bLength);
return LIBUSB_ERROR_IO;
}
- _bos = calloc (1,
- sizeof(*_bos) + bos_header.bNumDeviceCaps * sizeof(void *));
+ _bos = calloc(1, sizeof(*_bos) + bos_desc->bNumDeviceCaps * sizeof(void *));
if (!_bos)
return LIBUSB_ERROR_NO_MEM;
- usbi_parse_descriptor(buffer, "bbwb", _bos, host_endian);
- buffer += bos_header.bLength;
- size -= bos_header.bLength;
+ parse_descriptor(buffer, "bbwb", _bos);
+ buffer += _bos->bLength;
+ size -= _bos->bLength;
/* Get the device capability descriptors */
- for (i = 0; i < bos_header.bNumDeviceCaps; i++) {
+ for (i = 0; i < _bos->bNumDeviceCaps; i++) {
if (size < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) {
usbi_warn(ctx, "short dev-cap descriptor read %d/%d",
size, LIBUSB_DT_DEVICE_CAPABILITY_SIZE);
break;
}
- usbi_parse_descriptor(buffer, "bbb", &dev_cap, host_endian);
- if (dev_cap.bDescriptorType != LIBUSB_DT_DEVICE_CAPABILITY) {
- usbi_warn(ctx, "unexpected descriptor %x (expected %x)",
- dev_cap.bDescriptorType, LIBUSB_DT_DEVICE_CAPABILITY);
+ header = (const struct usbi_descriptor_header *)buffer;
+ if (header->bDescriptorType != LIBUSB_DT_DEVICE_CAPABILITY) {
+ usbi_warn(ctx, "unexpected descriptor 0x%x (expected 0x%x)",
+ header->bDescriptorType, LIBUSB_DT_DEVICE_CAPABILITY);
break;
- }
- if (dev_cap.bLength < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) {
- usbi_err(ctx, "invalid dev-cap bLength (%d)",
- dev_cap.bLength);
+ } else if (header->bLength < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) {
+ usbi_err(ctx, "invalid dev-cap bLength (%u)",
+ header->bLength);
libusb_free_bos_descriptor(_bos);
return LIBUSB_ERROR_IO;
- }
- if (dev_cap.bLength > size) {
- usbi_warn(ctx, "short dev-cap descriptor read %d/%d",
- size, dev_cap.bLength);
+ } else if (header->bLength > size) {
+ usbi_warn(ctx, "short dev-cap descriptor read %d/%u",
+ size, header->bLength);
break;
}
- _bos->dev_capability[i] = malloc(dev_cap.bLength);
+ _bos->dev_capability[i] = malloc(header->bLength);
if (!_bos->dev_capability[i]) {
libusb_free_bos_descriptor(_bos);
return LIBUSB_ERROR_NO_MEM;
}
- memcpy(_bos->dev_capability[i], buffer, dev_cap.bLength);
- buffer += dev_cap.bLength;
- size -= dev_cap.bLength;
+ memcpy(_bos->dev_capability[i], buffer, header->bLength);
+ buffer += header->bLength;
+ size -= header->bLength;
}
- _bos->bNumDeviceCaps = (uint8_t)i;
+ _bos->bNumDeviceCaps = i;
*bos = _bos;
return LIBUSB_SUCCESS;
int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle,
struct libusb_bos_descriptor **bos)
{
- struct libusb_bos_descriptor _bos;
- uint8_t bos_header[LIBUSB_DT_BOS_SIZE] = {0};
- unsigned char *bos_data = NULL;
- const int host_endian = 0;
+ union usbi_bos_desc_buf _bos;
+ uint16_t bos_len;
+ uint8_t *bos_data;
int r;
/* Read the BOS. This generates 2 requests on the bus,
* one for the header, and one for the full BOS */
- r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_header,
- LIBUSB_DT_BOS_SIZE);
+ r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, _bos.buf, sizeof(_bos.buf));
if (r < 0) {
if (r != LIBUSB_ERROR_PIPE)
usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r);
return LIBUSB_ERROR_IO;
}
- usbi_parse_descriptor(bos_header, "bbwb", &_bos, host_endian);
- usbi_dbg("found BOS descriptor: size %d bytes, %d capabilities",
- _bos.wTotalLength, _bos.bNumDeviceCaps);
- bos_data = calloc(_bos.wTotalLength, 1);
- if (bos_data == NULL)
+ bos_len = libusb_le16_to_cpu(_bos.desc.wTotalLength);
+ usbi_dbg("found BOS descriptor: size %u bytes, %u capabilities",
+ bos_len, _bos.desc.bNumDeviceCaps);
+ bos_data = calloc(1, bos_len);
+ if (!bos_data)
return LIBUSB_ERROR_NO_MEM;
- r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_data,
- _bos.wTotalLength);
- if (r >= 0)
- r = parse_bos(HANDLE_CTX(dev_handle), bos, bos_data, r, host_endian);
- else
+ r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_data, bos_len);
+ if (r >= 0) {
+ if (r != (int)bos_len)
+ usbi_warn(HANDLE_CTX(dev_handle), "short BOS read %d/%u",
+ r, bos_len);
+ r = parse_bos(HANDLE_CTX(dev_handle), bos, bos_data, r);
+ } else {
usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r);
+ }
free(bos_data);
return r;
*/
void API_EXPORTED libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos)
{
- int i;
+ uint8_t i;
if (!bos)
return;
* \returns a LIBUSB_ERROR code on error
*/
int API_EXPORTED libusb_get_usb_2_0_extension_descriptor(
- struct libusb_context *ctx,
+ libusb_context *ctx,
struct libusb_bos_dev_capability_descriptor *dev_cap,
struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension)
{
struct libusb_usb_2_0_extension_descriptor *_usb_2_0_extension;
- const int host_endian = 0;
if (dev_cap->bDevCapabilityType != LIBUSB_BT_USB_2_0_EXTENSION) {
- usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)",
+ usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)",
dev_cap->bDevCapabilityType,
LIBUSB_BT_USB_2_0_EXTENSION);
return LIBUSB_ERROR_INVALID_PARAM;
- }
- if (dev_cap->bLength < LIBUSB_BT_USB_2_0_EXTENSION_SIZE) {
- usbi_err(ctx, "short dev-cap descriptor read %d/%d",
+ } else if (dev_cap->bLength < LIBUSB_BT_USB_2_0_EXTENSION_SIZE) {
+ usbi_err(ctx, "short dev-cap descriptor read %u/%d",
dev_cap->bLength, LIBUSB_BT_USB_2_0_EXTENSION_SIZE);
return LIBUSB_ERROR_IO;
}
if (!_usb_2_0_extension)
return LIBUSB_ERROR_NO_MEM;
- usbi_parse_descriptor((unsigned char *)dev_cap, "bbbd",
- _usb_2_0_extension, host_endian);
+ parse_descriptor(dev_cap, "bbbd", _usb_2_0_extension);
*usb_2_0_extension = _usb_2_0_extension;
return LIBUSB_SUCCESS;
* \returns a LIBUSB_ERROR code on error
*/
int API_EXPORTED libusb_get_ss_usb_device_capability_descriptor(
- struct libusb_context *ctx,
+ libusb_context *ctx,
struct libusb_bos_dev_capability_descriptor *dev_cap,
struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap)
{
struct libusb_ss_usb_device_capability_descriptor *_ss_usb_device_cap;
- const int host_endian = 0;
if (dev_cap->bDevCapabilityType != LIBUSB_BT_SS_USB_DEVICE_CAPABILITY) {
- usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)",
+ usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)",
dev_cap->bDevCapabilityType,
LIBUSB_BT_SS_USB_DEVICE_CAPABILITY);
return LIBUSB_ERROR_INVALID_PARAM;
- }
- if (dev_cap->bLength < LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) {
- usbi_err(ctx, "short dev-cap descriptor read %d/%d",
+ } else if (dev_cap->bLength < LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) {
+ usbi_err(ctx, "short dev-cap descriptor read %u/%d",
dev_cap->bLength, LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE);
return LIBUSB_ERROR_IO;
}
if (!_ss_usb_device_cap)
return LIBUSB_ERROR_NO_MEM;
- usbi_parse_descriptor((unsigned char *)dev_cap, "bbbbwbbw",
- _ss_usb_device_cap, host_endian);
+ parse_descriptor(dev_cap, "bbbbwbbw", _ss_usb_device_cap);
*ss_usb_device_cap = _ss_usb_device_cap;
return LIBUSB_SUCCESS;
* It is safe to call this function with a NULL ss_usb_device_cap
* parameter, in which case the function simply returns.
*
- * \param ss_usb_device_cap the USB 2.0 Extension descriptor to free
+ * \param ss_usb_device_cap the SuperSpeed USB Device Capability descriptor
+ * to free
*/
void API_EXPORTED libusb_free_ss_usb_device_capability_descriptor(
struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap)
* \returns 0 on success
* \returns a LIBUSB_ERROR code on error
*/
-int API_EXPORTED libusb_get_container_id_descriptor(struct libusb_context *ctx,
+int API_EXPORTED libusb_get_container_id_descriptor(libusb_context *ctx,
struct libusb_bos_dev_capability_descriptor *dev_cap,
struct libusb_container_id_descriptor **container_id)
{
struct libusb_container_id_descriptor *_container_id;
- const int host_endian = 0;
if (dev_cap->bDevCapabilityType != LIBUSB_BT_CONTAINER_ID) {
- usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)",
+ usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)",
dev_cap->bDevCapabilityType,
LIBUSB_BT_CONTAINER_ID);
return LIBUSB_ERROR_INVALID_PARAM;
- }
- if (dev_cap->bLength < LIBUSB_BT_CONTAINER_ID_SIZE) {
- usbi_err(ctx, "short dev-cap descriptor read %d/%d",
+ } else if (dev_cap->bLength < LIBUSB_BT_CONTAINER_ID_SIZE) {
+ usbi_err(ctx, "short dev-cap descriptor read %u/%d",
dev_cap->bLength, LIBUSB_BT_CONTAINER_ID_SIZE);
return LIBUSB_ERROR_IO;
}
if (!_container_id)
return LIBUSB_ERROR_NO_MEM;
- usbi_parse_descriptor((unsigned char *)dev_cap, "bbbbu",
- _container_id, host_endian);
+ parse_descriptor(dev_cap, "bbbbu", _container_id);
*container_id = _container_id;
return LIBUSB_SUCCESS;
* It is safe to call this function with a NULL container_id parameter,
* in which case the function simply returns.
*
- * \param container_id the USB 2.0 Extension descriptor to free
+ * \param container_id the Container ID descriptor to free
*/
void API_EXPORTED libusb_free_container_id_descriptor(
struct libusb_container_id_descriptor *container_id)
int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle,
uint8_t desc_index, unsigned char *data, int length)
{
- unsigned char tbuf[255]; /* Some devices choke on size > 255 */
+ union usbi_string_desc_buf str;
int r, si, di;
- uint16_t langid;
+ uint16_t langid, wdata;
/* Asking for the zero'th index is special - it returns a string
* descriptor that contains all the language IDs supported by the
if (desc_index == 0)
return LIBUSB_ERROR_INVALID_PARAM;
- r = libusb_get_string_descriptor(dev_handle, 0, 0, tbuf, sizeof(tbuf));
+ r = libusb_get_string_descriptor(dev_handle, 0, 0, str.buf, 4);
if (r < 0)
return r;
-
- if (r < 4)
+ else if (r != 4 || str.desc.bLength < 4)
return LIBUSB_ERROR_IO;
+ else if (str.desc.bDescriptorType != LIBUSB_DT_STRING)
+ return LIBUSB_ERROR_IO;
+ else if (str.desc.bLength & 1)
+ usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor", str.desc.bLength);
- langid = tbuf[2] | (tbuf[3] << 8);
-
- r = libusb_get_string_descriptor(dev_handle, desc_index, langid, tbuf,
- sizeof(tbuf));
+ langid = libusb_le16_to_cpu(str.desc.wData[0]);
+ r = libusb_get_string_descriptor(dev_handle, desc_index, langid, str.buf, sizeof(str.buf));
if (r < 0)
return r;
-
- if (tbuf[1] != LIBUSB_DT_STRING)
+ else if (r < DESC_HEADER_LENGTH || str.desc.bLength > r)
return LIBUSB_ERROR_IO;
-
- if (tbuf[0] > r)
+ else if (str.desc.bDescriptorType != LIBUSB_DT_STRING)
return LIBUSB_ERROR_IO;
+ else if ((str.desc.bLength & 1) || str.desc.bLength != r)
+ usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor", str.desc.bLength);
di = 0;
- for (si = 2; si < tbuf[0]; si += 2) {
+ for (si = 2; si < str.desc.bLength; si += 2) {
if (di >= (length - 1))
break;
- if ((tbuf[si] & 0x80) || (tbuf[si + 1])) /* non-ASCII */
- data[di++] = '?';
+ wdata = libusb_le16_to_cpu(str.desc.wData[di]);
+ if (wdata < 0x80)
+ data[di++] = (unsigned char)wdata;
else
- data[di++] = tbuf[si];
+ data[di++] = '?'; /* non-ASCII */
}
data[di] = 0;
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <config.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#include <assert.h>
-
#include "libusbi.h"
#include "hotplug.h"
* Version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, has added support
* for hotplug events on <b>some</b> platforms (you should test if your platform
* supports hotplug notification by calling \ref libusb_has_capability() with
- * parameter \ref LIBUSB_CAP_HAS_HOTPLUG).
+ * parameter \ref LIBUSB_CAP_HAS_HOTPLUG).
*
* This interface allows you to request notification for the arrival and departure
* of matching USB devices.
* expecting additional events. Returning 0 will rearm the callback and 1 will cause
* the callback to be deregistered. Note that when callbacks are called from
* libusb_hotplug_register_callback() because of the \ref LIBUSB_HOTPLUG_ENUMERATE
- * flag, the callback return value is ignored, iow you cannot cause a callback
- * to be deregistered by returning 1 when it is called from
+ * flag, the callback return value is ignored. In other words, you cannot cause a
+ * callback to be deregistered by returning 1 when it is called from
* libusb_hotplug_register_callback().
*
* Callbacks for a particular context are automatically deregistered by libusb_exit().
\endcode
*/
+#define VALID_HOTPLUG_EVENTS \
+ (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | \
+ LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
+
+#define VALID_HOTPLUG_FLAGS \
+ (LIBUSB_HOTPLUG_ENUMERATE)
+
static int usbi_hotplug_match_cb(struct libusb_context *ctx,
struct libusb_device *dev, libusb_hotplug_event event,
struct libusb_hotplug_callback *hotplug_cb)
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
- list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
+ for_each_hotplug_cb_safe(ctx, hotplug_cb, next) {
if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) {
/* process deregistration in usbi_hotplug_deregister() */
continue;
void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
libusb_hotplug_event event)
{
- int pending_events;
struct libusb_hotplug_message *message = calloc(1, sizeof(*message));
+ unsigned int event_flags;
if (!message) {
usbi_err(ctx, "error allocating hotplug message");
/* Take the event data lock and add this message to the list.
* Only signal an event if there are no prior pending events. */
usbi_mutex_lock(&ctx->event_data_lock);
- pending_events = usbi_pending_events(ctx);
+ event_flags = ctx->event_flags;
+ ctx->event_flags |= USBI_EVENT_HOTPLUG_MSG_PENDING;
list_add_tail(&message->list, &ctx->hotplug_msgs);
- if (!pending_events)
- usbi_signal_event(ctx);
+ if (!event_flags)
+ usbi_signal_event(&ctx->event);
usbi_mutex_unlock(&ctx->event_data_lock);
}
int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
- libusb_hotplug_event events, libusb_hotplug_flag flags,
+ int events, int flags,
int vendor_id, int product_id, int dev_class,
libusb_hotplug_callback_fn cb_fn, void *user_data,
libusb_hotplug_callback_handle *callback_handle)
struct libusb_hotplug_callback *new_callback;
/* check for sane values */
- if ((!events || (~(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) & events)) ||
- (flags && (~LIBUSB_HOTPLUG_ENUMERATE & flags)) ||
+ if ((!events || (~VALID_HOTPLUG_EVENTS & events)) ||
+ (~VALID_HOTPLUG_FLAGS & flags) ||
(LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) ||
(LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) ||
(LIBUSB_HOTPLUG_MATCH_ANY != dev_class && (~0xff & dev_class)) ||
return LIBUSB_ERROR_NOT_SUPPORTED;
}
- USBI_GET_CONTEXT(ctx);
+ ctx = usbi_get_context(ctx);
new_callback = calloc(1, sizeof(*new_callback));
if (!new_callback) {
return LIBUSB_SUCCESS;
}
-void API_EXPORTED libusb_hotplug_deregister_callback(struct libusb_context *ctx,
+void API_EXPORTED libusb_hotplug_deregister_callback(libusb_context *ctx,
libusb_hotplug_callback_handle callback_handle)
{
struct libusb_hotplug_callback *hotplug_cb;
return;
}
- USBI_GET_CONTEXT(ctx);
-
usbi_dbg("deregister hotplug cb %d", callback_handle);
+ ctx = usbi_get_context(ctx);
+
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
- list_for_each_entry(hotplug_cb, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
+ for_each_hotplug_cb(ctx, hotplug_cb) {
if (callback_handle == hotplug_cb->handle) {
/* Mark this callback for deregistration */
hotplug_cb->flags |= USBI_HOTPLUG_NEEDS_FREE;
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
if (deregistered) {
- int pending_events;
+ unsigned int event_flags;
usbi_mutex_lock(&ctx->event_data_lock);
- pending_events = usbi_pending_events(ctx);
+ event_flags = ctx->event_flags;
ctx->event_flags |= USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
- if (!pending_events)
- usbi_signal_event(ctx);
+ if (!event_flags)
+ usbi_signal_event(&ctx->event);
usbi_mutex_unlock(&ctx->event_data_lock);
}
}
+DEFAULT_VISIBILITY
+void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx,
+ libusb_hotplug_callback_handle callback_handle)
+{
+ struct libusb_hotplug_callback *hotplug_cb;
+ void *user_data = NULL;
+
+ /* check for hotplug support */
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ return NULL;
+ }
+
+ usbi_dbg("get hotplug user data %d", callback_handle);
+
+ ctx = usbi_get_context(ctx);
+
+ usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+ for_each_hotplug_cb(ctx, hotplug_cb) {
+ if (callback_handle == hotplug_cb->handle) {
+ user_data = hotplug_cb->user_data;
+ }
+ }
+ usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+
+ return user_data;
+}
+
void usbi_hotplug_deregister(struct libusb_context *ctx, int forced)
{
struct libusb_hotplug_callback *hotplug_cb, *next;
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
- list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
+ for_each_hotplug_cb_safe(ctx, hotplug_cb, next) {
if (forced || (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)) {
usbi_dbg("freeing hotplug cb %p with handle %d", hotplug_cb,
hotplug_cb->handle);
*/
/* The vendor_id field is valid for matching */
- USBI_HOTPLUG_VENDOR_ID_VALID = (1 << 3),
+ USBI_HOTPLUG_VENDOR_ID_VALID = (1U << 3),
/* The product_id field is valid for matching */
- USBI_HOTPLUG_PRODUCT_ID_VALID = (1 << 4),
+ USBI_HOTPLUG_PRODUCT_ID_VALID = (1U << 4),
/* The dev_class field is valid for matching */
- USBI_HOTPLUG_DEV_CLASS_VALID = (1 << 5),
+ USBI_HOTPLUG_DEV_CLASS_VALID = (1U << 5),
/* This callback has been unregistered and needs to be freed */
- USBI_HOTPLUG_NEEDS_FREE = (1 << 6),
+ USBI_HOTPLUG_NEEDS_FREE = (1U << 6),
};
/** \ingroup hotplug
struct list_head list;
};
+#define for_each_hotplug_cb(ctx, c) \
+ for_each_helper(c, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback)
+
+#define for_each_hotplug_cb_safe(ctx, c, n) \
+ for_each_safe_helper(c, n, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback)
+
void usbi_hotplug_deregister(struct libusb_context *ctx, int forced);
void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
- libusb_hotplug_event event);
+ libusb_hotplug_event event);
void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
- libusb_hotplug_event event);
+ libusb_hotplug_event event);
#endif
* I/O functions for libusb
* Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org>
* Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
+ * Copyright © 2019 Nathan Hjelm <hjelmn@cs.umm.edu>
+ * Copyright © 2019 Google LLC. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <config.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#ifdef USBI_TIMERFD_AVAILABLE
-#include <sys/timerfd.h>
-#endif
-
#include "libusbi.h"
#include "hotplug.h"
* a single function call. When the function call returns, the transfer has
* completed and you can parse the results.
*
- * If you have used the libusb-0.1 before, this I/O style will seem familar to
+ * If you have used the libusb-0.1 before, this I/O style will seem familiar to
* you. libusb-0.1 only offered a synchronous interface.
*
* In our input device example, to read button presses you might write code
* sleeping for that long. Execution will be tied up inside the library -
* the entire thread will be useless for that duration.
*
- * Another issue is that by tieing up the thread with that single transaction
+ * Another issue is that by tying up the thread with that single transaction
* there is no possibility of performing I/O with multiple endpoints and/or
* multiple devices simultaneously, unless you resort to creating one thread
* per transaction.
* Freeing the transfer after it has been cancelled but before cancellation
* has completed will result in undefined behaviour.
*
+ * \attention
* When a transfer is cancelled, some of the data may have been transferred.
- * libusb will communicate this to you in the transfer callback. Do not assume
- * that no data was transferred.
+ * libusb will communicate this to you in the transfer callback.
+ * <b>Do not assume that no data was transferred.</b>
+ *
+ * \section asyncpartial Partial data transfer resulting from cancellation
+ *
+ * As noted above, some of the data may have been transferred at the time a
+ * transfer is cancelled. It is helpful to see how this is possible if you
+ * consider a bulk transfer to an endpoint with a packet size of 64 bytes.
+ * Supposing you submit a 512-byte transfer to this endpoint, the operating
+ * system will divide this transfer up into 8 separate 64-byte frames that the
+ * host controller will schedule for the device to transfer data. If this
+ * transfer is cancelled while the device is transferring data, a subset of
+ * these frames may be descheduled from the host controller before the device
+ * has the opportunity to finish transferring data to the host.
+ *
+ * What your application should do with a partial data transfer is a policy
+ * decision; there is no single answer that satisfies the needs of every
+ * application. The data that was successfully transferred should be
+ * considered entirely valid, but your application must decide what to do with
+ * the remaining data that was not transferred. Some possible actions to take
+ * are:
+ * - Resubmit another transfer for the remaining data, possibly with a shorter
+ * timeout
+ * - Discard the partially transferred data and report an error
+ *
+ * \section asynctimeout Timeouts
+ *
+ * When a transfer times out, libusb internally notes this and attempts to
+ * cancel the transfer. As noted in \ref asyncpartial "above", it is possible
+ * that some of the data may actually have been transferred. Your application
+ * should <b>always</b> check how much data was actually transferred once the
+ * transfer completes and act accordingly.
*
* \section bulk_overflows Overflows on device-to-host bulk/interrupt endpoints
*
* wLength of the setup packet, rather than the size of the data buffer. So,
* if your wLength was 4, your transfer's <tt>length</tt> was 12, then you
* should expect an <tt>actual_length</tt> of 4 to indicate that the data was
- * transferred in entirity.
+ * transferred in entirety.
*
* To simplify parsing of setup packets and obtaining the data from the
* correct offset, you may wish to use the libusb_control_transfer_get_data()
* libusb_get_iso_packet_buffer() and libusb_get_iso_packet_buffer_simple()
* functions may help you here.
*
- * <b>Note</b>: Some operating systems (e.g. Linux) may impose limits on the
- * length of individual isochronous packets and/or the total length of the
- * isochronous transfer. Such limits can be difficult for libusb to detect,
- * so the library will simply try and submit the transfer as set up by you.
- * If the transfer fails to submit because it is too large,
+ * \section asynclimits Transfer length limitations
+ *
+ * Some operating systems may impose limits on the length of the transfer data
+ * buffer or, in the case of isochronous transfers, the length of individual
+ * isochronous packets. Such limits can be difficult for libusb to detect, so
+ * in most cases the library will simply try and submit the transfer as set up
+ * by you. If the transfer fails to submit because it is too large,
* libusb_submit_transfer() will return
* \ref libusb_error::LIBUSB_ERROR_INVALID_PARAM "LIBUSB_ERROR_INVALID_PARAM".
*
+ * The following are known limits for control transfer lengths. Note that this
+ * length includes the 8-byte setup packet.
+ * - Linux (4,096 bytes)
+ * - Windows (4,096 bytes)
+ *
* \section asyncmem Memory caveats
*
* In most circumstances, it is not safe to use stack memory for transfer
* application must call into when libusb has work do to. This gives libusb
* the opportunity to reap pending transfers, invoke callbacks, etc.
*
- * There are 2 different approaches to dealing with libusb_handle_events:
+ * \note
+ * All event handling is performed by whichever thread calls the
+ * libusb_handle_events() function. libusb does not invoke any callbacks
+ * outside of this context. Consequently, any callbacks will be run on the
+ * thread that calls the libusb_handle_events() function.
+ *
+ * When to call the libusb_handle_events() function depends on which model
+ * your application decides to use. The 2 different approaches:
*
* -# Repeatedly call libusb_handle_events() in blocking mode from a dedicated
* thread.
*
* Lets begin with stating the obvious: If you're going to use a separate
* thread for libusb event handling, your callback functions MUST be
- * threadsafe.
+ * thread-safe.
*
* Other then that doing event handling from a separate thread, is mostly
* simple. You can use an event thread function as follows:
* system calls. This is directly exposed at the
* \ref libusb_asyncio "asynchronous interface" but it is important to note that the
* \ref libusb_syncio "synchronous interface" is implemented on top of the
- * asynchonrous interface, therefore the same considerations apply.
+ * asynchronous interface, therefore the same considerations apply.
*
* The issue is that if two or more threads are concurrently calling poll()
* or select() on libusb's file descriptors then only one of those threads
* do is submit a single transfer and wait for its completion, then using
* one of the synchronous I/O functions is much easier.
*
+ * \note
+ * The `completed` variable must be modified while holding the event lock,
+ * otherwise a race condition can still exist. It is simplest to do so from
+ * within the transfer callback as shown above.
+ *
* \section eventlock The events lock
*
* The problem is when we consider the fact that libusb exposes file
* event handling), because the event waiter seems to have taken the event
* waiters lock while waiting for an event. However, the system does support
* multiple event waiters, because libusb_wait_for_event() actually drops
- * the lock while waiting, and reaquires it before continuing.
+ * the lock while waiting, and reacquires it before continuing.
*
* We have now implemented code which can dynamically handle situations where
* nobody is handling events (so we should do it ourselves), and it can also
usbi_mutex_init(&ctx->event_data_lock);
usbi_tls_key_create(&ctx->event_handling_key);
list_init(&ctx->flying_transfers);
- list_init(&ctx->ipollfds);
+ list_init(&ctx->event_sources);
+ list_init(&ctx->removed_event_sources);
list_init(&ctx->hotplug_msgs);
list_init(&ctx->completed_transfers);
- /* FIXME should use an eventfd on kernels that support it */
- r = usbi_pipe(ctx->event_pipe);
- if (r < 0) {
- r = LIBUSB_ERROR_OTHER;
+ r = usbi_create_event(&ctx->event);
+ if (r < 0)
goto err;
- }
- r = usbi_add_pollfd(ctx, ctx->event_pipe[0], POLLIN);
+ r = usbi_add_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event), USBI_EVENT_POLL_EVENTS);
if (r < 0)
- goto err_close_pipe;
-
-#ifdef USBI_TIMERFD_AVAILABLE
- ctx->timerfd = timerfd_create(usbi_backend.get_timerfd_clockid(),
- TFD_NONBLOCK | TFD_CLOEXEC);
- if (ctx->timerfd >= 0) {
- usbi_dbg("using timerfd for timeouts");
- r = usbi_add_pollfd(ctx, ctx->timerfd, POLLIN);
+ goto err_destroy_event;
+
+#ifdef HAVE_OS_TIMER
+ r = usbi_create_timer(&ctx->timer);
+ if (r == 0) {
+ usbi_dbg("using timer for timeouts");
+ r = usbi_add_event_source(ctx, USBI_TIMER_OS_HANDLE(&ctx->timer), USBI_TIMER_POLL_EVENTS);
if (r < 0)
- goto err_close_timerfd;
+ goto err_destroy_timer;
} else {
- usbi_dbg("timerfd not available (code %d error %d)", ctx->timerfd, errno);
- ctx->timerfd = -1;
+ usbi_dbg("timer not available for timeouts");
}
#endif
return 0;
-#ifdef USBI_TIMERFD_AVAILABLE
-err_close_timerfd:
- close(ctx->timerfd);
- usbi_remove_pollfd(ctx, ctx->event_pipe[0]);
+#ifdef HAVE_OS_TIMER
+err_destroy_timer:
+ usbi_destroy_timer(&ctx->timer);
+ usbi_remove_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event));
#endif
-err_close_pipe:
- usbi_close(ctx->event_pipe[0]);
- usbi_close(ctx->event_pipe[1]);
+err_destroy_event:
+ usbi_destroy_event(&ctx->event);
err:
usbi_mutex_destroy(&ctx->flying_transfers_lock);
usbi_mutex_destroy(&ctx->events_lock);
return r;
}
+static void cleanup_removed_event_sources(struct libusb_context *ctx)
+{
+ struct usbi_event_source *ievent_source, *tmp;
+
+ for_each_removed_event_source_safe(ctx, ievent_source, tmp) {
+ list_del(&ievent_source->list);
+ free(ievent_source);
+ }
+}
+
void usbi_io_exit(struct libusb_context *ctx)
{
- usbi_remove_pollfd(ctx, ctx->event_pipe[0]);
- usbi_close(ctx->event_pipe[0]);
- usbi_close(ctx->event_pipe[1]);
-#ifdef USBI_TIMERFD_AVAILABLE
- if (usbi_using_timerfd(ctx)) {
- usbi_remove_pollfd(ctx, ctx->timerfd);
- close(ctx->timerfd);
+#ifdef HAVE_OS_TIMER
+ if (usbi_using_timer(ctx)) {
+ usbi_remove_event_source(ctx, USBI_TIMER_OS_HANDLE(&ctx->timer));
+ usbi_destroy_timer(&ctx->timer);
}
#endif
+ usbi_remove_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event));
+ usbi_destroy_event(&ctx->event);
usbi_mutex_destroy(&ctx->flying_transfers_lock);
usbi_mutex_destroy(&ctx->events_lock);
usbi_mutex_destroy(&ctx->event_waiters_lock);
usbi_cond_destroy(&ctx->event_waiters_cond);
usbi_mutex_destroy(&ctx->event_data_lock);
usbi_tls_key_delete(ctx->event_handling_key);
- if (ctx->pollfds)
- free(ctx->pollfds);
+ cleanup_removed_event_sources(ctx);
+ free(ctx->event_data);
}
-static int calculate_timeout(struct usbi_transfer *transfer)
+static void calculate_timeout(struct usbi_transfer *itransfer)
{
- int r;
- struct timespec current_time;
unsigned int timeout =
- USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout;
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout;
if (!timeout) {
- timerclear(&transfer->timeout);
- return 0;
- }
-
- r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, ¤t_time);
- if (r < 0) {
- usbi_err(ITRANSFER_CTX(transfer),
- "failed to read monotonic clock, errno=%d", errno);
- return r;
+ TIMESPEC_CLEAR(&itransfer->timeout);
+ return;
}
- current_time.tv_sec += timeout / 1000;
- current_time.tv_nsec += (timeout % 1000) * 1000000;
+ usbi_get_monotonic_time(&itransfer->timeout);
- while (current_time.tv_nsec >= 1000000000) {
- current_time.tv_nsec -= 1000000000;
- current_time.tv_sec++;
+ itransfer->timeout.tv_sec += timeout / 1000U;
+ itransfer->timeout.tv_nsec += (timeout % 1000U) * 1000000L;
+ if (itransfer->timeout.tv_nsec >= NSEC_PER_SEC) {
+ ++itransfer->timeout.tv_sec;
+ itransfer->timeout.tv_nsec -= NSEC_PER_SEC;
}
-
- TIMESPEC_TO_TIMEVAL(&transfer->timeout, ¤t_time);
- return 0;
}
/** \ingroup libusb_asyncio
* use it on a non-isochronous endpoint. If you do this, ensure that at time
* of submission, num_iso_packets is 0 and that type is set appropriately.
*
- * \param iso_packets number of isochronous packet descriptors to allocate
+ * \param iso_packets number of isochronous packet descriptors to allocate. Must be non-negative.
* \returns a newly allocated transfer, or NULL on error
*/
DEFAULT_VISIBILITY
struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(
int iso_packets)
{
+ size_t priv_size;
+ size_t alloc_size;
+ unsigned char *ptr;
+ struct usbi_transfer *itransfer;
struct libusb_transfer *transfer;
- size_t os_alloc_size = usbi_backend.transfer_priv_size;
- size_t alloc_size = sizeof(struct usbi_transfer)
+
+ assert(iso_packets >= 0);
+ if (iso_packets < 0)
+ return NULL;
+
+ priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size);
+ alloc_size = priv_size
+ + sizeof(struct usbi_transfer)
+ sizeof(struct libusb_transfer)
- + (sizeof(struct libusb_iso_packet_descriptor) * iso_packets)
- + os_alloc_size;
- struct usbi_transfer *itransfer = calloc(1, alloc_size);
- if (!itransfer)
+ + (sizeof(struct libusb_iso_packet_descriptor) * (size_t)iso_packets);
+ ptr = calloc(1, alloc_size);
+ if (!ptr)
return NULL;
+ itransfer = (struct usbi_transfer *)(ptr + priv_size);
itransfer->num_iso_packets = iso_packets;
+ itransfer->priv = ptr;
usbi_mutex_init(&itransfer->lock);
transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
usbi_dbg("transfer %p", transfer);
void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer)
{
struct usbi_transfer *itransfer;
+ size_t priv_size;
+ unsigned char *ptr;
+
if (!transfer)
return;
usbi_dbg("transfer %p", transfer);
- if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER && transfer->buffer)
+ if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER)
free(transfer->buffer);
itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
usbi_mutex_destroy(&itransfer->lock);
- free(itransfer);
-}
-#ifdef USBI_TIMERFD_AVAILABLE
-static int disarm_timerfd(struct libusb_context *ctx)
-{
- const struct itimerspec disarm_timer = { { 0, 0 }, { 0, 0 } };
- int r;
-
- usbi_dbg("");
- r = timerfd_settime(ctx->timerfd, 0, &disarm_timer, NULL);
- if (r < 0)
- return LIBUSB_ERROR_OTHER;
- else
- return 0;
+ priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size);
+ ptr = (unsigned char *)itransfer - priv_size;
+ assert(ptr == itransfer->priv);
+ free(ptr);
}
-/* iterates through the flying transfers, and rearms the timerfd based on the
+/* iterates through the flying transfers, and rearms the timer based on the
* next upcoming timeout.
* must be called with flying_list locked.
* returns 0 on success or a LIBUSB_ERROR code on failure.
*/
-static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
+#ifdef HAVE_OS_TIMER
+static int arm_timer_for_next_timeout(struct libusb_context *ctx)
{
- struct usbi_transfer *transfer;
+ struct usbi_transfer *itransfer;
+
+ if (!usbi_using_timer(ctx))
+ return 0;
- list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
- struct timeval *cur_tv = &transfer->timeout;
+ for_each_transfer(ctx, itransfer) {
+ struct timespec *cur_ts = &itransfer->timeout;
/* if we've reached transfers of infinite timeout, then we have no
* arming to do */
- if (!timerisset(cur_tv))
- goto disarm;
+ if (!TIMESPEC_IS_SET(cur_ts))
+ break;
/* act on first transfer that has not already been handled */
- if (!(transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) {
- int r;
- const struct itimerspec it = { {0, 0},
- { cur_tv->tv_sec, cur_tv->tv_usec * 1000 } };
- usbi_dbg("next timeout originally %dms", USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout);
- r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL);
- if (r < 0)
- return LIBUSB_ERROR_OTHER;
- return 0;
+ if (!(itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) {
+ usbi_dbg("next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
+ return usbi_arm_timer(&ctx->timer, cur_ts);
}
}
-disarm:
- return disarm_timerfd(ctx);
+ usbi_dbg("no timeouts, disarming timer");
+ return usbi_disarm_timer(&ctx->timer);
}
#else
-static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
+static inline int arm_timer_for_next_timeout(struct libusb_context *ctx)
{
UNUSED(ctx);
return 0;
/* add a transfer to the (timeout-sorted) active transfers list.
* This function will return non 0 if fails to update the timer,
* in which case the transfer is *not* on the flying_transfers list. */
-static int add_to_flying_list(struct usbi_transfer *transfer)
+static int add_to_flying_list(struct usbi_transfer *itransfer)
{
struct usbi_transfer *cur;
- struct timeval *timeout = &transfer->timeout;
- struct libusb_context *ctx = ITRANSFER_CTX(transfer);
- int r;
+ struct timespec *timeout = &itransfer->timeout;
+ struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
+ int r = 0;
int first = 1;
- r = calculate_timeout(transfer);
- if (r)
- return r;
+ calculate_timeout(itransfer);
/* if we have no other flying transfers, start the list with this one */
if (list_empty(&ctx->flying_transfers)) {
- list_add(&transfer->list, &ctx->flying_transfers);
+ list_add(&itransfer->list, &ctx->flying_transfers);
goto out;
}
/* if we have infinite timeout, append to end of list */
- if (!timerisset(timeout)) {
- list_add_tail(&transfer->list, &ctx->flying_transfers);
+ if (!TIMESPEC_IS_SET(timeout)) {
+ list_add_tail(&itransfer->list, &ctx->flying_transfers);
/* first is irrelevant in this case */
goto out;
}
/* otherwise, find appropriate place in list */
- list_for_each_entry(cur, &ctx->flying_transfers, list, struct usbi_transfer) {
+ for_each_transfer(ctx, cur) {
/* find first timeout that occurs after the transfer in question */
- struct timeval *cur_tv = &cur->timeout;
+ struct timespec *cur_ts = &cur->timeout;
- if (!timerisset(cur_tv) || (cur_tv->tv_sec > timeout->tv_sec) ||
- (cur_tv->tv_sec == timeout->tv_sec &&
- cur_tv->tv_usec > timeout->tv_usec)) {
- list_add_tail(&transfer->list, &cur->list);
+ if (!TIMESPEC_IS_SET(cur_ts) || TIMESPEC_CMP(cur_ts, timeout, >)) {
+ list_add_tail(&itransfer->list, &cur->list);
goto out;
}
first = 0;
/* first is 0 at this stage (list not empty) */
/* otherwise we need to be inserted at the end */
- list_add_tail(&transfer->list, &ctx->flying_transfers);
+ list_add_tail(&itransfer->list, &ctx->flying_transfers);
out:
-#ifdef USBI_TIMERFD_AVAILABLE
- if (first && usbi_using_timerfd(ctx) && timerisset(timeout)) {
+#ifdef HAVE_OS_TIMER
+ if (first && usbi_using_timer(ctx) && TIMESPEC_IS_SET(timeout)) {
/* if this transfer has the lowest timeout of all active transfers,
- * rearm the timerfd with this transfer's timeout */
- const struct itimerspec it = { {0, 0},
- { timeout->tv_sec, timeout->tv_usec * 1000 } };
- usbi_dbg("arm timerfd for timeout in %dms (first in line)",
- USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout);
- r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL);
- if (r < 0) {
- usbi_warn(ctx, "failed to arm first timerfd (errno %d)", errno);
- r = LIBUSB_ERROR_OTHER;
- }
+ * rearm the timer with this transfer's timeout */
+ usbi_dbg("arm timer for timeout in %ums (first in line)",
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
+ r = usbi_arm_timer(&ctx->timer, timeout);
}
#else
UNUSED(first);
#endif
if (r)
- list_del(&transfer->list);
+ list_del(&itransfer->list);
return r;
}
* This function will *always* remove the transfer from the
* flying_transfers list. It will return a LIBUSB_ERROR code
* if it fails to update the timer for the next timeout. */
-static int remove_from_flying_list(struct usbi_transfer *transfer)
+static int remove_from_flying_list(struct usbi_transfer *itransfer)
{
- struct libusb_context *ctx = ITRANSFER_CTX(transfer);
- int rearm_timerfd;
+ struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
+ int rearm_timer;
int r = 0;
usbi_mutex_lock(&ctx->flying_transfers_lock);
- rearm_timerfd = (timerisset(&transfer->timeout) &&
- list_first_entry(&ctx->flying_transfers, struct usbi_transfer, list) == transfer);
- list_del(&transfer->list);
- if (usbi_using_timerfd(ctx) && rearm_timerfd)
- r = arm_timerfd_for_next_timeout(ctx);
+ rearm_timer = (TIMESPEC_IS_SET(&itransfer->timeout) &&
+ list_first_entry(&ctx->flying_transfers, struct usbi_transfer, list) == itransfer);
+ list_del(&itransfer->list);
+ if (rearm_timer)
+ r = arm_timer_for_next_timeout(ctx);
usbi_mutex_unlock(&ctx->flying_transfers_lock);
return r;
* \returns LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported
* by the operating system.
* \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
- * the operating system and/or hardware can support
+ * the operating system and/or hardware can support (see \ref asynclimits)
* \returns another LIBUSB_ERROR code on other failure
*/
int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer)
r = remove_from_flying_list(itransfer);
if (r < 0)
- usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout, errno=%d", errno);
+ usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout");
usbi_mutex_lock(&itransfer->lock);
itransfer->state_flags &= ~USBI_TRANSFER_IN_FLIGHT;
* Do not call this function with the usbi_transfer lock held. User-specified
* callback functions may attempt to directly resubmit the transfer, which
* will attempt to take the lock. */
-int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer)
+int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer)
{
- struct libusb_context *ctx = ITRANSFER_CTX(transfer);
+ struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
uint8_t timed_out;
usbi_mutex_lock(&ctx->flying_transfers_lock);
- timed_out = transfer->timeout_flags & USBI_TRANSFER_TIMED_OUT;
+ timed_out = itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT;
usbi_mutex_unlock(&ctx->flying_transfers_lock);
/* if the URB was cancelled due to timeout, report timeout to the user */
if (timed_out) {
usbi_dbg("detected timeout cancellation");
- return usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_TIMED_OUT);
+ return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_TIMED_OUT);
}
/* otherwise its a normal async cancel */
- return usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_CANCELLED);
+ return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_CANCELLED);
}
/* Add a completed transfer to the completed_transfers list of the
* context and signal the event. The backend's handle_transfer_completion()
* function will be called the next time an event handler runs. */
-void usbi_signal_transfer_completion(struct usbi_transfer *transfer)
+void usbi_signal_transfer_completion(struct usbi_transfer *itransfer)
{
- struct libusb_context *ctx = ITRANSFER_CTX(transfer);
- int pending_events;
+ libusb_device_handle *dev_handle = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->dev_handle;
- usbi_mutex_lock(&ctx->event_data_lock);
- pending_events = usbi_pending_events(ctx);
- list_add_tail(&transfer->completed_list, &ctx->completed_transfers);
- if (!pending_events)
- usbi_signal_event(ctx);
- usbi_mutex_unlock(&ctx->event_data_lock);
+ if (dev_handle) {
+ struct libusb_context *ctx = HANDLE_CTX(dev_handle);
+ unsigned int event_flags;
+
+ usbi_mutex_lock(&ctx->event_data_lock);
+ event_flags = ctx->event_flags;
+ ctx->event_flags |= USBI_EVENT_TRANSFER_COMPLETED;
+ list_add_tail(&itransfer->completed_list, &ctx->completed_transfers);
+ if (!event_flags)
+ usbi_signal_event(&ctx->event);
+ usbi_mutex_unlock(&ctx->event_data_lock);
+ }
}
/** \ingroup libusb_poll
{
int r;
unsigned int ru;
- USBI_GET_CONTEXT(ctx);
+
+ ctx = usbi_get_context(ctx);
/* is someone else waiting to close a device? if so, don't let this thread
* start event handling */
}
r = usbi_mutex_trylock(&ctx->events_lock);
- if (r)
+ if (!r)
return 1;
ctx->event_handler_active = 1;
*/
void API_EXPORTED libusb_lock_events(libusb_context *ctx)
{
- USBI_GET_CONTEXT(ctx);
+ ctx = usbi_get_context(ctx);
usbi_mutex_lock(&ctx->events_lock);
ctx->event_handler_active = 1;
}
*/
void API_EXPORTED libusb_unlock_events(libusb_context *ctx)
{
- USBI_GET_CONTEXT(ctx);
+ ctx = usbi_get_context(ctx);
ctx->event_handler_active = 0;
usbi_mutex_unlock(&ctx->events_lock);
int API_EXPORTED libusb_event_handling_ok(libusb_context *ctx)
{
unsigned int r;
- USBI_GET_CONTEXT(ctx);
+
+ ctx = usbi_get_context(ctx);
/* is someone else waiting to close a device? if so, don't let this thread
* continue event handling */
int API_EXPORTED libusb_event_handler_active(libusb_context *ctx)
{
unsigned int r;
- USBI_GET_CONTEXT(ctx);
+
+ ctx = usbi_get_context(ctx);
/* is someone else waiting to close a device? if so, don't let this thread
* start event handling -- indicate that event handling is happening */
*/
void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx)
{
- int pending_events;
- USBI_GET_CONTEXT(ctx);
+ unsigned int event_flags;
- usbi_dbg("");
+ usbi_dbg(" ");
+
+ ctx = usbi_get_context(ctx);
usbi_mutex_lock(&ctx->event_data_lock);
- pending_events = usbi_pending_events(ctx);
+ event_flags = ctx->event_flags;
ctx->event_flags |= USBI_EVENT_USER_INTERRUPT;
- if (!pending_events)
- usbi_signal_event(ctx);
+ if (!event_flags)
+ usbi_signal_event(&ctx->event);
usbi_mutex_unlock(&ctx->event_data_lock);
}
*
* You only need to use this lock if you are developing an application
* which calls poll() or select() on libusb's file descriptors directly,
- * <b>and</b> may potentially be handling events from 2 threads simultaenously.
+ * <b>and</b> may potentially be handling events from 2 threads simultaneously.
* If you stick to libusb's event handling loop functions (e.g.
* libusb_handle_events()) then you do not need to be concerned with this
* locking.
*/
void API_EXPORTED libusb_lock_event_waiters(libusb_context *ctx)
{
- USBI_GET_CONTEXT(ctx);
+ ctx = usbi_get_context(ctx);
usbi_mutex_lock(&ctx->event_waiters_lock);
}
*/
void API_EXPORTED libusb_unlock_event_waiters(libusb_context *ctx)
{
- USBI_GET_CONTEXT(ctx);
+ ctx = usbi_get_context(ctx);
usbi_mutex_unlock(&ctx->event_waiters_lock);
}
* indicates unlimited timeout.
* \returns 0 after a transfer completes or another thread stops event handling
* \returns 1 if the timeout expired
+ * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
* \ref libusb_mtasync
*/
int API_EXPORTED libusb_wait_for_event(libusb_context *ctx, struct timeval *tv)
{
int r;
- USBI_GET_CONTEXT(ctx);
- if (tv == NULL) {
+ ctx = usbi_get_context(ctx);
+ if (!tv) {
usbi_cond_wait(&ctx->event_waiters_cond, &ctx->event_waiters_lock);
return 0;
}
+ if (!TIMEVAL_IS_VALID(tv))
+ return LIBUSB_ERROR_INVALID_PARAM;
+
r = usbi_cond_timedwait(&ctx->event_waiters_cond,
&ctx->event_waiters_lock, tv);
-
if (r < 0)
- return r;
- else
- return (r == ETIMEDOUT);
+ return r == LIBUSB_ERROR_TIMEOUT;
+
+ return 0;
}
static void handle_timeout(struct usbi_transfer *itransfer)
itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT;
else
usbi_warn(TRANSFER_CTX(transfer),
- "async cancel failed %d errno=%d", r, errno);
+ "async cancel failed %d", r);
}
-static int handle_timeouts_locked(struct libusb_context *ctx)
+static void handle_timeouts_locked(struct libusb_context *ctx)
{
- int r;
- struct timespec systime_ts;
- struct timeval systime;
- struct usbi_transfer *transfer;
+ struct timespec systime;
+ struct usbi_transfer *itransfer;
if (list_empty(&ctx->flying_transfers))
- return 0;
+ return;
/* get current time */
- r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, &systime_ts);
- if (r < 0)
- return r;
-
- TIMESPEC_TO_TIMEVAL(&systime, &systime_ts);
+ usbi_get_monotonic_time(&systime);
/* iterate through flying transfers list, finding all transfers that
* have expired timeouts */
- list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
- struct timeval *cur_tv = &transfer->timeout;
+ for_each_transfer(ctx, itransfer) {
+ struct timespec *cur_ts = &itransfer->timeout;
/* if we've reached transfers of infinite timeout, we're all done */
- if (!timerisset(cur_tv))
- return 0;
+ if (!TIMESPEC_IS_SET(cur_ts))
+ return;
/* ignore timeouts we've already handled */
- if (transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
+ if (itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
continue;
/* if transfer has non-expired timeout, nothing more to do */
- if ((cur_tv->tv_sec > systime.tv_sec) ||
- (cur_tv->tv_sec == systime.tv_sec &&
- cur_tv->tv_usec > systime.tv_usec))
- return 0;
+ if (TIMESPEC_CMP(cur_ts, &systime, >))
+ return;
/* otherwise, we've got an expired timeout to handle */
- handle_timeout(transfer);
+ handle_timeout(itransfer);
}
- return 0;
}
-static int handle_timeouts(struct libusb_context *ctx)
+static void handle_timeouts(struct libusb_context *ctx)
{
- int r;
- USBI_GET_CONTEXT(ctx);
+ ctx = usbi_get_context(ctx);
usbi_mutex_lock(&ctx->flying_transfers_lock);
- r = handle_timeouts_locked(ctx);
+ handle_timeouts_locked(ctx);
usbi_mutex_unlock(&ctx->flying_transfers_lock);
+}
+
+static int handle_event_trigger(struct libusb_context *ctx)
+{
+ struct list_head hotplug_msgs;
+ int r = 0;
+
+ usbi_dbg("event triggered");
+
+ list_init(&hotplug_msgs);
+
+ /* take the the event data lock while processing events */
+ usbi_mutex_lock(&ctx->event_data_lock);
+
+ /* check if someone modified the event sources */
+ if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED)
+ usbi_dbg("someone updated the event sources");
+
+ if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) {
+ usbi_dbg("someone purposefully interrupted");
+ ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT;
+ }
+
+ /* check if someone is closing a device */
+ if (ctx->event_flags & USBI_EVENT_DEVICE_CLOSE)
+ usbi_dbg("someone is closing a device");
+
+ /* check for any pending hotplug messages */
+ if (ctx->event_flags & USBI_EVENT_HOTPLUG_MSG_PENDING) {
+ usbi_dbg("hotplug message received");
+ ctx->event_flags &= ~USBI_EVENT_HOTPLUG_MSG_PENDING;
+ assert(!list_empty(&ctx->hotplug_msgs));
+ list_cut(&hotplug_msgs, &ctx->hotplug_msgs);
+ }
+
+ /* complete any pending transfers */
+ if (ctx->event_flags & USBI_EVENT_TRANSFER_COMPLETED) {
+ struct usbi_transfer *itransfer, *tmp;
+ struct list_head completed_transfers;
+
+ assert(!list_empty(&ctx->completed_transfers));
+ list_cut(&completed_transfers, &ctx->completed_transfers);
+ usbi_mutex_unlock(&ctx->event_data_lock);
+
+ __for_each_completed_transfer_safe(&completed_transfers, itransfer, tmp) {
+ list_del(&itransfer->completed_list);
+ r = usbi_backend.handle_transfer_completion(itransfer);
+ if (r) {
+ usbi_err(ctx, "backend handle_transfer_completion failed with error %d", r);
+ break;
+ }
+ }
+
+ usbi_mutex_lock(&ctx->event_data_lock);
+ if (!list_empty(&completed_transfers)) {
+ /* an error occurred, put the remaining transfers back on the list */
+ list_splice_front(&completed_transfers, &ctx->completed_transfers);
+ } else if (list_empty(&ctx->completed_transfers)) {
+ ctx->event_flags &= ~USBI_EVENT_TRANSFER_COMPLETED;
+ }
+ }
+
+ /* if no further pending events, clear the event */
+ if (!ctx->event_flags)
+ usbi_clear_event(&ctx->event);
+
+ usbi_mutex_unlock(&ctx->event_data_lock);
+
+ /* process the hotplug messages, if any */
+ while (!list_empty(&hotplug_msgs)) {
+ struct libusb_hotplug_message *message =
+ list_first_entry(&hotplug_msgs, struct libusb_hotplug_message, list);
+
+ usbi_hotplug_match(ctx, message->device, message->event);
+
+ /* the device left, dereference the device */
+ if (message->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
+ libusb_unref_device(message->device);
+
+ list_del(&message->list);
+ free(message);
+ }
+
return r;
}
-#ifdef USBI_TIMERFD_AVAILABLE
-static int handle_timerfd_trigger(struct libusb_context *ctx)
+#ifdef HAVE_OS_TIMER
+static int handle_timer_trigger(struct libusb_context *ctx)
{
int r;
usbi_mutex_lock(&ctx->flying_transfers_lock);
/* process the timeout that just happened */
- r = handle_timeouts_locked(ctx);
- if (r < 0)
- goto out;
+ handle_timeouts_locked(ctx);
- /* arm for next timeout*/
- r = arm_timerfd_for_next_timeout(ctx);
+ /* arm for next timeout */
+ r = arm_timer_for_next_timeout(ctx);
-out:
usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
return r;
}
#endif
* doing the same thing. */
static int handle_events(struct libusb_context *ctx, struct timeval *tv)
{
- int r;
- struct usbi_pollfd *ipollfd;
- POLL_NFDS_TYPE nfds = 0;
- POLL_NFDS_TYPE internal_nfds;
- struct pollfd *fds = NULL;
- int i = -1;
- int timeout_ms;
+ struct usbi_reported_events reported_events;
+ int r, timeout_ms;
/* prevent attempts to recursively handle events (e.g. calling into
* libusb_handle_events() from within a hotplug or transfer callback) */
if (usbi_handling_events(ctx))
return LIBUSB_ERROR_BUSY;
- usbi_start_event_handling(ctx);
- /* there are certain fds that libusb uses internally, currently:
- *
- * 1) event pipe
- * 2) timerfd
- *
- * the backend will never need to attempt to handle events on these fds, so
- * we determine how many fds are in use internally for this context and when
- * handle_events() is called in the backend, the pollfd list and count will
- * be adjusted to skip over these internal fds */
- if (usbi_using_timerfd(ctx))
- internal_nfds = 2;
- else
- internal_nfds = 1;
-
- /* only reallocate the poll fds when the list of poll fds has been modified
- * since the last poll, otherwise reuse them to save the additional overhead */
+ /* only reallocate the event source data when the list of event sources has
+ * been modified since the last handle_events(), otherwise reuse them to
+ * save the additional overhead */
usbi_mutex_lock(&ctx->event_data_lock);
- if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED) {
- usbi_dbg("poll fds modified, reallocating");
+ if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) {
+ usbi_dbg("event sources modified, reallocating event data");
- if (ctx->pollfds) {
- free(ctx->pollfds);
- ctx->pollfds = NULL;
- }
-
- /* sanity check - it is invalid for a context to have fewer than the
- * required internal fds (memory corruption?) */
- assert(ctx->pollfds_cnt >= internal_nfds);
+ /* free anything removed since we last ran */
+ cleanup_removed_event_sources(ctx);
- ctx->pollfds = calloc(ctx->pollfds_cnt, sizeof(*ctx->pollfds));
- if (!ctx->pollfds) {
+ r = usbi_alloc_event_data(ctx);
+ if (r) {
usbi_mutex_unlock(&ctx->event_data_lock);
- r = LIBUSB_ERROR_NO_MEM;
- goto done;
- }
-
- list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd) {
- struct libusb_pollfd *pollfd = &ipollfd->pollfd;
- i++;
- ctx->pollfds[i].fd = pollfd->fd;
- ctx->pollfds[i].events = pollfd->events;
+ return r;
}
/* reset the flag now that we have the updated list */
- ctx->event_flags &= ~USBI_EVENT_POLLFDS_MODIFIED;
+ ctx->event_flags &= ~USBI_EVENT_EVENT_SOURCES_MODIFIED;
- /* if no further pending events, clear the event pipe so that we do
- * not immediately return from poll */
- if (!usbi_pending_events(ctx))
- usbi_clear_event(ctx);
+ /* if no further pending events, clear the event so that we do
+ * not immediately return from the wait function */
+ if (!ctx->event_flags)
+ usbi_clear_event(&ctx->event);
}
- fds = ctx->pollfds;
- nfds = ctx->pollfds_cnt;
usbi_mutex_unlock(&ctx->event_data_lock);
timeout_ms = (int)(tv->tv_sec * 1000) + (tv->tv_usec / 1000);
if (tv->tv_usec % 1000)
timeout_ms++;
- usbi_dbg("poll() %d fds with timeout in %dms", nfds, timeout_ms);
- r = usbi_poll(fds, nfds, timeout_ms);
- usbi_dbg("poll() returned %d", r);
- if (r == 0) {
- r = handle_timeouts(ctx);
- goto done;
- } else if (r == -1 && errno == EINTR) {
- r = LIBUSB_ERROR_INTERRUPTED;
- goto done;
- } else if (r < 0) {
- usbi_err(ctx, "poll failed %d err=%d", r, errno);
- r = LIBUSB_ERROR_IO;
- goto done;
- }
-
- /* fds[0] is always the event pipe */
- if (fds[0].revents) {
- struct list_head hotplug_msgs;
- struct usbi_transfer *itransfer;
- int hotplug_cb_deregistered = 0;
- int ret = 0;
-
- list_init(&hotplug_msgs);
-
- usbi_dbg("caught a fish on the event pipe");
-
- /* take the the event data lock while processing events */
- usbi_mutex_lock(&ctx->event_data_lock);
-
- /* check if someone added a new poll fd */
- if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED)
- usbi_dbg("someone updated the poll fds");
+ reported_events.event_bits = 0;
- if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) {
- usbi_dbg("someone purposely interrupted");
- ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT;
- }
-
- if (ctx->event_flags & USBI_EVENT_HOTPLUG_CB_DEREGISTERED) {
- usbi_dbg("someone unregistered a hotplug cb");
- ctx->event_flags &= ~USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
- hotplug_cb_deregistered = 1;
- }
-
- /* check if someone is closing a device */
- if (ctx->device_close)
- usbi_dbg("someone is closing a device");
-
- /* check for any pending hotplug messages */
- if (!list_empty(&ctx->hotplug_msgs)) {
- usbi_dbg("hotplug message received");
- list_cut(&hotplug_msgs, &ctx->hotplug_msgs);
- }
-
- /* complete any pending transfers */
- while (ret == 0 && !list_empty(&ctx->completed_transfers)) {
- itransfer = list_first_entry(&ctx->completed_transfers, struct usbi_transfer, completed_list);
- list_del(&itransfer->completed_list);
- usbi_mutex_unlock(&ctx->event_data_lock);
- ret = usbi_backend.handle_transfer_completion(itransfer);
- if (ret)
- usbi_err(ctx, "backend handle_transfer_completion failed with error %d", ret);
- usbi_mutex_lock(&ctx->event_data_lock);
- }
-
- /* if no further pending events, clear the event pipe */
- if (!usbi_pending_events(ctx))
- usbi_clear_event(ctx);
-
- usbi_mutex_unlock(&ctx->event_data_lock);
-
- if (hotplug_cb_deregistered)
- usbi_hotplug_deregister(ctx, 0);
-
- /* process the hotplug messages, if any */
- while (!list_empty(&hotplug_msgs)) {
- struct libusb_hotplug_message *message =
- list_first_entry(&hotplug_msgs, struct libusb_hotplug_message, list);
-
- usbi_hotplug_match(ctx, message->device, message->event);
-
- /* the device left, dereference the device */
- if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == message->event)
- libusb_unref_device(message->device);
+ usbi_start_event_handling(ctx);
- list_del(&message->list);
- free(message);
+ r = usbi_wait_for_events(ctx, &reported_events, timeout_ms);
+ if (r != LIBUSB_SUCCESS) {
+ if (r == LIBUSB_ERROR_TIMEOUT) {
+ handle_timeouts(ctx);
+ r = LIBUSB_SUCCESS;
}
+ goto done;
+ }
- if (ret) {
+ if (reported_events.event_triggered) {
+ r = handle_event_trigger(ctx);
+ if (r) {
/* return error code */
- r = ret;
goto done;
}
-
- if (0 == --r)
- goto done;
}
-#ifdef USBI_TIMERFD_AVAILABLE
- /* on timerfd configurations, fds[1] is the timerfd */
- if (usbi_using_timerfd(ctx) && fds[1].revents) {
- /* timerfd indicates that a timeout has expired */
- int ret;
- usbi_dbg("timerfd triggered");
-
- ret = handle_timerfd_trigger(ctx);
- if (ret < 0) {
+#ifdef HAVE_OS_TIMER
+ if (reported_events.timer_triggered) {
+ r = handle_timer_trigger(ctx);
+ if (r) {
/* return error code */
- r = ret;
goto done;
}
-
- if (0 == --r)
- goto done;
}
#endif
- r = usbi_backend.handle_events(ctx, fds + internal_nfds, nfds - internal_nfds, r);
+ if (!reported_events.num_ready)
+ goto done;
+
+ r = usbi_backend.handle_events(ctx, reported_events.event_data,
+ reported_events.event_data_count, reported_events.num_ready);
if (r)
usbi_err(ctx, "backend handle_events failed with error %d", r);
* \param tv the maximum time to block waiting for events, or an all zero
* timeval struct for non-blocking mode
* \param completed pointer to completion integer to check, or NULL
- * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
+ * \returns another LIBUSB_ERROR code on other failure
* \ref libusb_mtasync
*/
int API_EXPORTED libusb_handle_events_timeout_completed(libusb_context *ctx,
int r;
struct timeval poll_timeout;
- USBI_GET_CONTEXT(ctx);
+ if (!TIMEVAL_IS_VALID(tv))
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ ctx = usbi_get_context(ctx);
r = get_next_timeout(ctx, tv, &poll_timeout);
if (r) {
/* timeout already expired */
- return handle_timeouts(ctx);
+ handle_timeouts(ctx);
+ return 0;
}
retry:
if (r < 0)
return r;
else if (r == 1)
- return handle_timeouts(ctx);
- else
- return 0;
+ handle_timeouts(ctx);
+ return 0;
}
/** \ingroup libusb_poll
/** \ingroup libusb_poll
* Handle any pending events in blocking mode. There is currently a timeout
- * hardcoded at 60 seconds but we plan to make it unlimited in future. For
+ * hard-coded at 60 seconds but we plan to make it unlimited in future. For
* finer control over whether this function is blocking or non-blocking, or
* for control over the timeout, use libusb_handle_events_timeout_completed()
* instead.
* \param ctx the context to operate on, or NULL for the default context
* \param tv the maximum time to block waiting for events, or zero for
* non-blocking mode
- * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
+ * \returns another LIBUSB_ERROR code on other failure
* \ref libusb_mtasync
*/
int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx,
int r;
struct timeval poll_timeout;
- USBI_GET_CONTEXT(ctx);
+ if (!TIMEVAL_IS_VALID(tv))
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ ctx = usbi_get_context(ctx);
r = get_next_timeout(ctx, tv, &poll_timeout);
if (r) {
/* timeout already expired */
- return handle_timeouts(ctx);
+ handle_timeouts(ctx);
+ return 0;
}
return handle_events(ctx, &poll_timeout);
*/
int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx)
{
-#if defined(USBI_TIMERFD_AVAILABLE)
- USBI_GET_CONTEXT(ctx);
- return usbi_using_timerfd(ctx);
-#else
- UNUSED(ctx);
- return 0;
-#endif
+ ctx = usbi_get_context(ctx);
+ return usbi_using_timer(ctx);
}
/** \ingroup libusb_poll
int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
struct timeval *tv)
{
- struct usbi_transfer *transfer;
- struct timespec cur_ts;
- struct timeval cur_tv;
- struct timeval next_timeout = { 0, 0 };
- int r;
+ struct usbi_transfer *itransfer;
+ struct timespec systime;
+ struct timespec next_timeout = { 0, 0 };
- USBI_GET_CONTEXT(ctx);
- if (usbi_using_timerfd(ctx))
+ ctx = usbi_get_context(ctx);
+ if (usbi_using_timer(ctx))
return 0;
usbi_mutex_lock(&ctx->flying_transfers_lock);
}
/* find next transfer which hasn't already been processed as timed out */
- list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
- if (transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
+ for_each_transfer(ctx, itransfer) {
+ if (itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
continue;
- /* if we've reached transfers of infinte timeout, we're done looking */
- if (!timerisset(&transfer->timeout))
+ /* if we've reached transfers of infinite timeout, we're done looking */
+ if (!TIMESPEC_IS_SET(&itransfer->timeout))
break;
- next_timeout = transfer->timeout;
+ next_timeout = itransfer->timeout;
break;
}
usbi_mutex_unlock(&ctx->flying_transfers_lock);
- if (!timerisset(&next_timeout)) {
+ if (!TIMESPEC_IS_SET(&next_timeout)) {
usbi_dbg("no URB with timeout or all handled by OS; no timeout!");
return 0;
}
- r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, &cur_ts);
- if (r < 0) {
- usbi_err(ctx, "failed to read monotonic clock, errno=%d", errno);
- return 0;
- }
- TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts);
+ usbi_get_monotonic_time(&systime);
- if (!timercmp(&cur_tv, &next_timeout, <)) {
+ if (!TIMESPEC_CMP(&systime, &next_timeout, <)) {
usbi_dbg("first timeout already expired");
timerclear(tv);
} else {
- timersub(&next_timeout, &cur_tv, tv);
- usbi_dbg("next timeout in %d.%06ds", tv->tv_sec, tv->tv_usec);
+ TIMESPEC_SUB(&next_timeout, &systime, &next_timeout);
+ TIMESPEC_TO_TIMEVAL(tv, &next_timeout);
+ usbi_dbg("next timeout in %ld.%06lds", (long)tv->tv_sec, (long)tv->tv_usec);
}
return 1;
libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb,
void *user_data)
{
- USBI_GET_CONTEXT(ctx);
+#if !defined(PLATFORM_WINDOWS)
+ ctx = usbi_get_context(ctx);
ctx->fd_added_cb = added_cb;
ctx->fd_removed_cb = removed_cb;
ctx->fd_cb_user_data = user_data;
+#else
+ usbi_err(ctx, "external polling of libusb's internal event sources " \
+ "is not yet supported on Windows");
+ UNUSED(added_cb);
+ UNUSED(removed_cb);
+ UNUSED(user_data);
+#endif
}
/*
* Interrupt the iteration of the event handling thread, so that it picks
- * up the fd change. Callers of this function must hold the event_data_lock.
+ * up the event source change. Callers of this function must hold the event_data_lock.
*/
-static void usbi_fd_notification(struct libusb_context *ctx)
+static void usbi_event_source_notification(struct libusb_context *ctx)
{
- int pending_events;
+ unsigned int event_flags;
/* Record that there is a new poll fd.
* Only signal an event if there are no prior pending events. */
- pending_events = usbi_pending_events(ctx);
- ctx->event_flags |= USBI_EVENT_POLLFDS_MODIFIED;
- if (!pending_events)
- usbi_signal_event(ctx);
+ event_flags = ctx->event_flags;
+ ctx->event_flags |= USBI_EVENT_EVENT_SOURCES_MODIFIED;
+ if (!event_flags)
+ usbi_signal_event(&ctx->event);
}
-/* Add a file descriptor to the list of file descriptors to be monitored.
- * events should be specified as a bitmask of events passed to poll(), e.g.
+/* Add an event source to the list of event sources to be monitored.
+ * poll_events should be specified as a bitmask of events passed to poll(), e.g.
* POLLIN and/or POLLOUT. */
-int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events)
+int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle, short poll_events)
{
- struct usbi_pollfd *ipollfd = malloc(sizeof(*ipollfd));
- if (!ipollfd)
+ struct usbi_event_source *ievent_source = malloc(sizeof(*ievent_source));
+
+ if (!ievent_source)
return LIBUSB_ERROR_NO_MEM;
- usbi_dbg("add fd %d events %d", fd, events);
- ipollfd->pollfd.fd = fd;
- ipollfd->pollfd.events = events;
+ usbi_dbg("add " USBI_OS_HANDLE_FORMAT_STRING " events %d", os_handle, poll_events);
+ ievent_source->data.os_handle = os_handle;
+ ievent_source->data.poll_events = poll_events;
usbi_mutex_lock(&ctx->event_data_lock);
- list_add_tail(&ipollfd->list, &ctx->ipollfds);
- ctx->pollfds_cnt++;
- usbi_fd_notification(ctx);
+ list_add_tail(&ievent_source->list, &ctx->event_sources);
+ usbi_event_source_notification(ctx);
usbi_mutex_unlock(&ctx->event_data_lock);
+#if !defined(PLATFORM_WINDOWS)
if (ctx->fd_added_cb)
- ctx->fd_added_cb(fd, events, ctx->fd_cb_user_data);
+ ctx->fd_added_cb(os_handle, poll_events, ctx->fd_cb_user_data);
+#endif
+
return 0;
}
-/* Remove a file descriptor from the list of file descriptors to be polled. */
-void usbi_remove_pollfd(struct libusb_context *ctx, int fd)
+/* Remove an event source from the list of event sources to be monitored. */
+void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle)
{
- struct usbi_pollfd *ipollfd;
+ struct usbi_event_source *ievent_source;
int found = 0;
- usbi_dbg("remove fd %d", fd);
+ usbi_dbg("remove " USBI_OS_HANDLE_FORMAT_STRING, os_handle);
usbi_mutex_lock(&ctx->event_data_lock);
- list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd)
- if (ipollfd->pollfd.fd == fd) {
+ for_each_event_source(ctx, ievent_source) {
+ if (ievent_source->data.os_handle == os_handle) {
found = 1;
break;
}
+ }
if (!found) {
- usbi_dbg("couldn't find fd %d to remove", fd);
+ usbi_dbg("couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle);
usbi_mutex_unlock(&ctx->event_data_lock);
return;
}
- list_del(&ipollfd->list);
- ctx->pollfds_cnt--;
- usbi_fd_notification(ctx);
+ list_del(&ievent_source->list);
+ list_add_tail(&ievent_source->list, &ctx->removed_event_sources);
+ usbi_event_source_notification(ctx);
usbi_mutex_unlock(&ctx->event_data_lock);
- free(ipollfd);
+
+#if !defined(PLATFORM_WINDOWS)
if (ctx->fd_removed_cb)
- ctx->fd_removed_cb(fd, ctx->fd_cb_user_data);
+ ctx->fd_removed_cb(os_handle, ctx->fd_cb_user_data);
+#endif
}
/** \ingroup libusb_poll
const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds(
libusb_context *ctx)
{
-#ifndef OS_WINDOWS
+#if !defined(PLATFORM_WINDOWS)
struct libusb_pollfd **ret = NULL;
- struct usbi_pollfd *ipollfd;
- size_t i = 0;
- USBI_GET_CONTEXT(ctx);
+ struct usbi_event_source *ievent_source;
+ size_t i;
+
+ static_assert(sizeof(struct usbi_event_source_data) == sizeof(struct libusb_pollfd),
+ "mismatch between usbi_event_source_data and libusb_pollfd sizes");
+
+ ctx = usbi_get_context(ctx);
usbi_mutex_lock(&ctx->event_data_lock);
- ret = calloc(ctx->pollfds_cnt + 1, sizeof(struct libusb_pollfd *));
+ i = 0;
+ for_each_event_source(ctx, ievent_source)
+ i++;
+
+ ret = calloc(i + 1, sizeof(struct libusb_pollfd *));
if (!ret)
goto out;
- list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd)
- ret[i++] = (struct libusb_pollfd *) ipollfd;
- ret[ctx->pollfds_cnt] = NULL;
+ i = 0;
+ for_each_event_source(ctx, ievent_source)
+ ret[i++] = (struct libusb_pollfd *)ievent_source;
out:
usbi_mutex_unlock(&ctx->event_data_lock);
- return (const struct libusb_pollfd **) ret;
+ return (const struct libusb_pollfd **)ret;
#else
- usbi_err(ctx, "external polling of libusb's internal descriptors "\
- "is not yet supported on Windows platforms");
+ usbi_err(ctx, "external polling of libusb's internal event sources " \
+ "is not yet supported on Windows");
return NULL;
#endif
}
* Since version 1.0.20, \ref LIBUSB_API_VERSION >= 0x01000104
*
* It is legal to call this function with a NULL pollfd list. In this case,
- * the function will simply return safely.
+ * the function will simply do nothing.
*
* \param pollfds the list of libusb_pollfd structures to free
*/
void API_EXPORTED libusb_free_pollfds(const struct libusb_pollfd **pollfds)
{
- if (!pollfds)
- return;
-
+#if !defined(PLATFORM_WINDOWS)
free((void *)pollfds);
+#else
+ UNUSED(pollfds);
+#endif
}
/* Backends may call this from handle_events to report disconnection of a
*/
void usbi_handle_disconnect(struct libusb_device_handle *dev_handle)
{
+ struct libusb_context *ctx = HANDLE_CTX(dev_handle);
struct usbi_transfer *cur;
struct usbi_transfer *to_cancel;
while (1) {
to_cancel = NULL;
- usbi_mutex_lock(&HANDLE_CTX(dev_handle)->flying_transfers_lock);
- list_for_each_entry(cur, &HANDLE_CTX(dev_handle)->flying_transfers, list, struct usbi_transfer)
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+ for_each_transfer(ctx, cur) {
if (USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur)->dev_handle == dev_handle) {
usbi_mutex_lock(&cur->lock);
if (cur->state_flags & USBI_TRANSFER_IN_FLIGHT)
if (to_cancel)
break;
}
- usbi_mutex_unlock(&HANDLE_CTX(dev_handle)->flying_transfers_lock);
+ }
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
if (!to_cancel)
break;
usbi_mutex_unlock(&to_cancel->lock);
usbi_handle_transfer_completion(to_cancel, LIBUSB_TRANSFER_NO_DEVICE);
}
-
}
* Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
* Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright © 2012 Pete Batard <pete@akeo.ie>
- * Copyright © 2012 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2012-2018 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2014-2020 Chris Dickens <christopher.a.dickens@gmail.com>
* For more information, please visit: http://libusb.info
*
* This library is free software; you can redistribute it and/or
#ifndef LIBUSB_H
#define LIBUSB_H
-#ifdef _MSC_VER
+#if defined(_MSC_VER)
/* on MS environments, the inline keyword is available in C++ only */
#if !defined(__cplusplus)
#define inline __inline
#endif
-/* ssize_t is also not available (copy/paste from MinGW) */
-#ifndef _SSIZE_T_DEFINED
-#define _SSIZE_T_DEFINED
-#undef ssize_t
-#ifdef _WIN64
- typedef __int64 ssize_t;
-#else
- typedef int ssize_t;
-#endif /* _WIN64 */
-#endif /* _SSIZE_T_DEFINED */
+/* ssize_t is also not available */
+#include <basetsd.h>
+typedef SSIZE_T ssize_t;
#endif /* _MSC_VER */
-/* stdint.h is not available on older MSVC */
-#if defined(_MSC_VER) && (_MSC_VER < 1600) && (!defined(_STDINT)) && (!defined(_STDINT_H))
-typedef unsigned __int8 uint8_t;
-typedef unsigned __int16 uint16_t;
-typedef unsigned __int32 uint32_t;
-#else
+#include <limits.h>
#include <stdint.h>
-#endif
-
-#if !defined(_WIN32_WCE)
#include <sys/types.h>
-#endif
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__HAIKU__)
+#if !defined(_MSC_VER)
#include <sys/time.h>
#endif
-
#include <time.h>
-#include <limits.h>
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
#define ZERO_SIZED_ARRAY /* [] - valid C99 code */
#else
#define ZERO_SIZED_ARRAY 0 /* [0] - non-standard, but usually working code */
-#endif
+#endif /* __STDC_VERSION__ */
/* 'interface' might be defined as a macro on Windows, so we need to
* undefine it so as not to break the current libusb API, because
* libusb_config_descriptor has an 'interface' member
* As this can be problematic if you include windows.h after libusb.h
* in your sources, we force windows.h to be included first. */
-#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+#if defined(_WIN32) || defined(__CYGWIN__)
#include <windows.h>
#if defined(interface)
#undef interface
#if !defined(__CYGWIN__)
#include <winsock.h>
#endif
-#endif
+#endif /* _WIN32 || __CYGWIN__ */
-#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
-#define LIBUSB_DEPRECATED_FOR(f) \
- __attribute__((deprecated("Use " #f " instead")))
-#elif __GNUC__ >= 3
-#define LIBUSB_DEPRECATED_FOR(f) __attribute__((deprecated))
+#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
+#define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated ("Use " #f " instead")))
+#elif defined(__GNUC__) && (__GNUC__ >= 3)
+#define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated))
#else
#define LIBUSB_DEPRECATED_FOR(f)
#endif /* __GNUC__ */
+#if defined(__GNUC__)
+#define LIBUSB_PACKED __attribute__ ((packed))
+#else
+#define LIBUSB_PACKED
+#endif /* __GNUC__ */
+
/** \def LIBUSB_CALL
* \ingroup libusb_misc
* libusb's Windows calling convention.
* return type, before the function name. See internal documentation for
* API_EXPORTED.
*/
-#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+#if defined(_WIN32) || defined(__CYGWIN__)
#define LIBUSB_CALL WINAPI
#else
#define LIBUSB_CALL
-#endif
+#endif /* _WIN32 || __CYGWIN__ */
/** \def LIBUSB_API_VERSION
* \ingroup libusb_misc
* Internally, LIBUSB_API_VERSION is defined as follows:
* (libusb major << 24) | (libusb minor << 16) | (16 bit incremental)
*/
-#define LIBUSB_API_VERSION 0x01000106
+#define LIBUSB_API_VERSION 0x01000108
/* The following is kept for compatibility, but will be deprecated in the future */
#define LIBUSBX_API_VERSION LIBUSB_API_VERSION
-#ifdef __cplusplus
+#if defined(__cplusplus)
extern "C" {
#endif
* this bDeviceClass value indicates that each interface specifies its
* own class information and all interfaces operate independently.
*/
- LIBUSB_CLASS_PER_INTERFACE = 0,
+ LIBUSB_CLASS_PER_INTERFACE = 0x00,
/** Audio class */
- LIBUSB_CLASS_AUDIO = 1,
+ LIBUSB_CLASS_AUDIO = 0x01,
/** Communications class */
- LIBUSB_CLASS_COMM = 2,
+ LIBUSB_CLASS_COMM = 0x02,
/** Human Interface Device class */
- LIBUSB_CLASS_HID = 3,
+ LIBUSB_CLASS_HID = 0x03,
/** Physical */
- LIBUSB_CLASS_PHYSICAL = 5,
-
- /** Printer class */
- LIBUSB_CLASS_PRINTER = 7,
+ LIBUSB_CLASS_PHYSICAL = 0x05,
/** Image class */
- LIBUSB_CLASS_PTP = 6, /* legacy name from libusb-0.1 usb.h */
- LIBUSB_CLASS_IMAGE = 6,
+ LIBUSB_CLASS_IMAGE = 0x06,
+ LIBUSB_CLASS_PTP = 0x06, /* legacy name from libusb-0.1 usb.h */
+
+ /** Printer class */
+ LIBUSB_CLASS_PRINTER = 0x07,
/** Mass storage class */
- LIBUSB_CLASS_MASS_STORAGE = 8,
+ LIBUSB_CLASS_MASS_STORAGE = 0x08,
/** Hub class */
- LIBUSB_CLASS_HUB = 9,
+ LIBUSB_CLASS_HUB = 0x09,
/** Data class */
- LIBUSB_CLASS_DATA = 10,
+ LIBUSB_CLASS_DATA = 0x0a,
/** Smart Card */
LIBUSB_CLASS_SMART_CARD = 0x0b,
/** Wireless class */
LIBUSB_CLASS_WIRELESS = 0xe0,
+ /** Miscellaneous class */
+ LIBUSB_CLASS_MISCELLANEOUS = 0xef,
+
/** Application class */
LIBUSB_CLASS_APPLICATION = 0xfe,
#define LIBUSB_BT_CONTAINER_ID_SIZE 20
/* We unwrap the BOS => define its max size */
-#define LIBUSB_DT_BOS_MAX_SIZE ((LIBUSB_DT_BOS_SIZE) +\
- (LIBUSB_BT_USB_2_0_EXTENSION_SIZE) +\
- (LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) +\
- (LIBUSB_BT_CONTAINER_ID_SIZE))
+#define LIBUSB_DT_BOS_MAX_SIZE \
+ (LIBUSB_DT_BOS_SIZE + \
+ LIBUSB_BT_USB_2_0_EXTENSION_SIZE + \
+ LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE + \
+ LIBUSB_BT_CONTAINER_ID_SIZE)
-#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */
+#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */
#define LIBUSB_ENDPOINT_DIR_MASK 0x80
/** \ingroup libusb_desc
* \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme.
*/
enum libusb_endpoint_direction {
- /** In: device-to-host */
- LIBUSB_ENDPOINT_IN = 0x80,
-
/** Out: host-to-device */
- LIBUSB_ENDPOINT_OUT = 0x00
+ LIBUSB_ENDPOINT_OUT = 0x00,
+
+ /** In: device-to-host */
+ LIBUSB_ENDPOINT_IN = 0x80
};
-#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */
+#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */
/** \ingroup libusb_desc
* Endpoint transfer type. Values for bits 0:1 of the
* \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field.
*/
-enum libusb_transfer_type {
+enum libusb_endpoint_transfer_type {
/** Control endpoint */
- LIBUSB_TRANSFER_TYPE_CONTROL = 0,
+ LIBUSB_ENDPOINT_TRANSFER_TYPE_CONTROL = 0x0,
/** Isochronous endpoint */
- LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1,
+ LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS = 0x1,
/** Bulk endpoint */
- LIBUSB_TRANSFER_TYPE_BULK = 2,
+ LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK = 0x2,
/** Interrupt endpoint */
- LIBUSB_TRANSFER_TYPE_INTERRUPT = 3,
-
- /** Stream endpoint */
- LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4,
+ LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT = 0x3
};
/** \ingroup libusb_misc
LIBUSB_REQUEST_SET_CONFIGURATION = 0x09,
/** Return the selected alternate setting for the specified interface */
- LIBUSB_REQUEST_GET_INTERFACE = 0x0A,
+ LIBUSB_REQUEST_GET_INTERFACE = 0x0a,
/** Select an alternate interface for the specified interface */
- LIBUSB_REQUEST_SET_INTERFACE = 0x0B,
+ LIBUSB_REQUEST_SET_INTERFACE = 0x0b,
/** Set then report an endpoint's synchronization frame */
- LIBUSB_REQUEST_SYNCH_FRAME = 0x0C,
+ LIBUSB_REQUEST_SYNCH_FRAME = 0x0c,
/** Sets both the U1 and U2 Exit Latency */
LIBUSB_REQUEST_SET_SEL = 0x30,
/** Delay from the time a host transmits a packet to the time it is
* received by the device. */
- LIBUSB_SET_ISOCH_DELAY = 0x31,
+ LIBUSB_SET_ISOCH_DELAY = 0x31
};
/** \ingroup libusb_misc
LIBUSB_RECIPIENT_ENDPOINT = 0x02,
/** Other */
- LIBUSB_RECIPIENT_OTHER = 0x03,
+ LIBUSB_RECIPIENT_OTHER = 0x03
};
-#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C
+#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0c
/** \ingroup libusb_desc
* Synchronization type for isochronous endpoints. Values for bits 2:3 of the
*/
enum libusb_iso_sync_type {
/** No synchronization */
- LIBUSB_ISO_SYNC_TYPE_NONE = 0,
+ LIBUSB_ISO_SYNC_TYPE_NONE = 0x0,
/** Asynchronous */
- LIBUSB_ISO_SYNC_TYPE_ASYNC = 1,
+ LIBUSB_ISO_SYNC_TYPE_ASYNC = 0x1,
/** Adaptive */
- LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2,
+ LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 0x2,
/** Synchronous */
- LIBUSB_ISO_SYNC_TYPE_SYNC = 3
+ LIBUSB_ISO_SYNC_TYPE_SYNC = 0x3
};
-#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30
+#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30
/** \ingroup libusb_desc
* Usage type for isochronous endpoints. Values for bits 4:5 of the
*/
enum libusb_iso_usage_type {
/** Data endpoint */
- LIBUSB_ISO_USAGE_TYPE_DATA = 0,
+ LIBUSB_ISO_USAGE_TYPE_DATA = 0x0,
/** Feedback endpoint */
- LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1,
+ LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 0x1,
/** Implicit feedback Data endpoint */
- LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2,
+ LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 0x2
+};
+
+/** \ingroup libusb_desc
+ * Supported speeds (wSpeedSupported) bitfield. Indicates what
+ * speeds the device supports.
+ */
+enum libusb_supported_speed {
+ /** Low speed operation supported (1.5MBit/s). */
+ LIBUSB_LOW_SPEED_OPERATION = (1 << 0),
+
+ /** Full speed operation supported (12MBit/s). */
+ LIBUSB_FULL_SPEED_OPERATION = (1 << 1),
+
+ /** High speed operation supported (480MBit/s). */
+ LIBUSB_HIGH_SPEED_OPERATION = (1 << 2),
+
+ /** Superspeed operation supported (5000MBit/s). */
+ LIBUSB_SUPER_SPEED_OPERATION = (1 << 3)
+};
+
+/** \ingroup libusb_desc
+ * Masks for the bits of the
+ * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field
+ * of the USB 2.0 Extension descriptor.
+ */
+enum libusb_usb_2_0_extension_attributes {
+ /** Supports Link Power Management (LPM) */
+ LIBUSB_BM_LPM_SUPPORT = (1 << 1)
+};
+
+/** \ingroup libusb_desc
+ * Masks for the bits of the
+ * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field
+ * field of the SuperSpeed USB Device Capability descriptor.
+ */
+enum libusb_ss_usb_device_capability_attributes {
+ /** Supports Latency Tolerance Messages (LTM) */
+ LIBUSB_BM_LTM_SUPPORT = (1 << 1)
+};
+
+/** \ingroup libusb_desc
+ * USB capability types
+ */
+enum libusb_bos_type {
+ /** Wireless USB device capability */
+ LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 0x01,
+
+ /** USB 2.0 extensions */
+ LIBUSB_BT_USB_2_0_EXTENSION = 0x02,
+
+ /** SuperSpeed USB device capability */
+ LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 0x03,
+
+ /** Container ID type */
+ LIBUSB_BT_CONTAINER_ID = 0x04
};
/** \ingroup libusb_desc
/** The address of the endpoint described by this descriptor. Bits 0:3 are
* the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction,
- * see \ref libusb_endpoint_direction.
- */
+ * see \ref libusb_endpoint_direction. */
uint8_t bEndpointAddress;
/** Attributes which apply to the endpoint when it is configured using
* the bConfigurationValue. Bits 0:1 determine the transfer type and
- * correspond to \ref libusb_transfer_type. Bits 2:3 are only used for
- * isochronous endpoints and correspond to \ref libusb_iso_sync_type.
+ * correspond to \ref libusb_endpoint_transfer_type. Bits 2:3 are only used
+ * for isochronous endpoints and correspond to \ref libusb_iso_sync_type.
* Bits 4:5 are also only used for isochronous endpoints and correspond to
- * \ref libusb_iso_usage_type. Bits 6:7 are reserved.
- */
+ * \ref libusb_iso_usage_type. Bits 6:7 are reserved. */
uint8_t bmAttributes;
/** Maximum packet size this endpoint is capable of sending/receiving. */
* it will store them here, should you wish to parse them. */
const unsigned char *extra;
- /** Length of the extra descriptors, in bytes. */
+ /** Length of the extra descriptors, in bytes. Must be non-negative. */
int extra_length;
};
* it will store them here, should you wish to parse them. */
const unsigned char *extra;
- /** Length of the extra descriptors, in bytes. */
+ /** Length of the extra descriptors, in bytes. Must be non-negative. */
int extra_length;
};
* by the num_altsetting field. */
const struct libusb_interface_descriptor *altsetting;
- /** The number of alternate settings that belong to this interface */
+ /** The number of alternate settings that belong to this interface.
+ * Must be non-negative. */
int num_altsetting;
};
* descriptors, it will store them here, should you wish to parse them. */
const unsigned char *extra;
- /** Length of the extra descriptors, in bytes. */
+ /** Length of the extra descriptors, in bytes. Must be non-negative. */
int extra_length;
};
* host-endian format.
*/
struct libusb_ss_endpoint_companion_descriptor {
-
/** Size of this descriptor (in bytes) */
uint8_t bLength;
* this context. */
uint8_t bDescriptorType;
-
/** The maximum number of packets the endpoint can send or
* receive as part of a burst. */
uint8_t bMaxBurst;
- /** In bulk EP: bits 4:0 represents the maximum number of
- * streams the EP supports. In isochronous EP: bits 1:0
- * represents the Mult - a zero based value that determines
- * the maximum number of packets within a service interval */
+ /** In bulk EP: bits 4:0 represents the maximum number of
+ * streams the EP supports. In isochronous EP: bits 1:0
+ * represents the Mult - a zero based value that determines
+ * the maximum number of packets within a service interval */
uint8_t bmAttributes;
- /** The total number of bytes this EP will transfer every
- * service interval. valid only for periodic EPs. */
+ /** The total number of bytes this EP will transfer every
+ * service interval. Valid only for periodic EPs. */
uint16_t wBytesPerInterval;
};
*/
struct libusb_bos_dev_capability_descriptor {
/** Size of this descriptor (in bytes) */
- uint8_t bLength;
+ uint8_t bLength;
+
/** Descriptor type. Will have value
* \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
* LIBUSB_DT_DEVICE_CAPABILITY in this context. */
- uint8_t bDescriptorType;
+ uint8_t bDescriptorType;
+
/** Device Capability type */
- uint8_t bDevCapabilityType;
+ uint8_t bDevCapabilityType;
+
/** Device Capability data (bLength - 3 bytes) */
- uint8_t dev_capability_data[ZERO_SIZED_ARRAY];
+ uint8_t dev_capability_data[ZERO_SIZED_ARRAY];
};
/** \ingroup libusb_desc
* A value of one in a bit location indicates a feature is
* supported; a value of zero indicates it is not supported.
* See \ref libusb_usb_2_0_extension_attributes. */
- uint32_t bmAttributes;
+ uint32_t bmAttributes;
};
/** \ingroup libusb_desc
uint8_t bDevCapabilityType;
/** Reserved field */
- uint8_t bReserved;
+ uint8_t bReserved;
/** 128 bit UUID */
uint8_t ContainerID[16];
/** \ingroup libusb_asyncio
* Setup packet for control transfers. */
+#if defined(_MSC_VER)
+#pragma pack(push, 1)
+#endif
struct libusb_control_setup {
/** Request type. Bits 0:4 determine recipient, see
* \ref libusb_request_recipient. Bits 5:6 determine type, see
/** Number of bytes to transfer */
uint16_t wLength;
-};
+} LIBUSB_PACKED;
+#if defined(_MSC_VER)
+#pragma pack(pop)
+#endif
#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup))
const char *rc;
/** For ABI compatibility only. */
- const char* describe;
+ const char *describe;
};
/** \ingroup libusb_lib
LIBUSB_SPEED_SUPER = 4,
/** The device is operating at super speed plus (10000MBit/s). */
- LIBUSB_SPEED_SUPER_PLUS = 5,
-};
-
-/** \ingroup libusb_dev
- * Supported speeds (wSpeedSupported) bitfield. Indicates what
- * speeds the device supports.
- */
-enum libusb_supported_speed {
- /** Low speed operation supported (1.5MBit/s). */
- LIBUSB_LOW_SPEED_OPERATION = 1,
-
- /** Full speed operation supported (12MBit/s). */
- LIBUSB_FULL_SPEED_OPERATION = 2,
-
- /** High speed operation supported (480MBit/s). */
- LIBUSB_HIGH_SPEED_OPERATION = 4,
-
- /** Superspeed operation supported (5000MBit/s). */
- LIBUSB_SUPER_SPEED_OPERATION = 8,
-};
-
-/** \ingroup libusb_dev
- * Masks for the bits of the
- * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field
- * of the USB 2.0 Extension descriptor.
- */
-enum libusb_usb_2_0_extension_attributes {
- /** Supports Link Power Management (LPM) */
- LIBUSB_BM_LPM_SUPPORT = 2,
-};
-
-/** \ingroup libusb_dev
- * Masks for the bits of the
- * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field
- * field of the SuperSpeed USB Device Capability descriptor.
- */
-enum libusb_ss_usb_device_capability_attributes {
- /** Supports Latency Tolerance Messages (LTM) */
- LIBUSB_BM_LTM_SUPPORT = 2,
-};
-
-/** \ingroup libusb_dev
- * USB capability types
- */
-enum libusb_bos_type {
- /** Wireless USB device capability */
- LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 1,
-
- /** USB 2.0 extensions */
- LIBUSB_BT_USB_2_0_EXTENSION = 2,
-
- /** SuperSpeed USB device capability */
- LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 3,
-
- /** Container ID type */
- LIBUSB_BT_CONTAINER_ID = 4,
+ LIBUSB_SPEED_SUPER_PLUS = 5
};
/** \ingroup libusb_misc
message strings in strerror.c when adding new error codes here. */
/** Other error */
- LIBUSB_ERROR_OTHER = -99,
+ LIBUSB_ERROR_OTHER = -99
};
/* Total number of error codes in enum libusb_error */
#define LIBUSB_ERROR_COUNT 14
+/** \ingroup libusb_asyncio
+ * Transfer type */
+enum libusb_transfer_type {
+ /** Control transfer */
+ LIBUSB_TRANSFER_TYPE_CONTROL = 0U,
+
+ /** Isochronous transfer */
+ LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1U,
+
+ /** Bulk transfer */
+ LIBUSB_TRANSFER_TYPE_BULK = 2U,
+
+ /** Interrupt transfer */
+ LIBUSB_TRANSFER_TYPE_INTERRUPT = 3U,
+
+ /** Bulk stream transfer */
+ LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4U
+};
+
/** \ingroup libusb_asyncio
* Transfer status codes */
enum libusb_transfer_status {
LIBUSB_TRANSFER_NO_DEVICE,
/** Device sent more data than requested */
- LIBUSB_TRANSFER_OVERFLOW,
+ LIBUSB_TRANSFER_OVERFLOW
/* NB! Remember to update libusb_error_name()
when adding new status codes here. */
* libusb_transfer.flags values */
enum libusb_transfer_flags {
/** Report short frames as errors */
- LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0,
+ LIBUSB_TRANSFER_SHORT_NOT_OK = (1U << 0),
/** Automatically free() transfer buffer during libusb_free_transfer().
* Note that buffers allocated with libusb_dev_mem_alloc() should not
* be attempted freed in this way, since free() is not an appropriate
* way to release such memory. */
- LIBUSB_TRANSFER_FREE_BUFFER = 1<<1,
+ LIBUSB_TRANSFER_FREE_BUFFER = (1U << 1),
/** Automatically call libusb_free_transfer() after callback returns.
* If this flag is set, it is illegal to call libusb_free_transfer()
* from your transfer callback, as this will result in a double-free
* when this flag is acted upon. */
- LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2,
+ LIBUSB_TRANSFER_FREE_TRANSFER = (1U << 2),
/** Terminate transfers that are a multiple of the endpoint's
* wMaxPacketSize with an extra zero length packet. This is useful
*
* Available since libusb-1.0.9.
*/
- LIBUSB_TRANSFER_ADD_ZERO_PACKET = 1 << 3,
+ LIBUSB_TRANSFER_ADD_ZERO_PACKET = (1U << 3)
};
/** \ingroup libusb_asyncio
/** Address of the endpoint where this transfer will be sent. */
unsigned char endpoint;
- /** Type of the endpoint from \ref libusb_transfer_type */
+ /** Type of the transfer from \ref libusb_transfer_type */
unsigned char type;
/** Timeout for this transfer in milliseconds. A value of 0 indicates no
* to determine if errors occurred. */
enum libusb_transfer_status status;
- /** Length of the data buffer */
+ /** Length of the data buffer. Must be non-negative. */
int length;
/** Actual length of data that was transferred. Read-only, and only for
* fails, or is cancelled. */
libusb_transfer_cb_fn callback;
- /** User context data to pass to the callback function. */
+ /** User context data. Useful for associating specific data to a transfer
+ * that can be accessed from within the callback function.
+ *
+ * This field may be set manually or is taken as the `user_data` parameter
+ * of the following functions:
+ * - libusb_fill_bulk_transfer()
+ * - libusb_fill_bulk_stream_transfer()
+ * - libusb_fill_control_transfer()
+ * - libusb_fill_interrupt_transfer()
+ * - libusb_fill_iso_transfer() */
void *user_data;
/** Data buffer */
unsigned char *buffer;
/** Number of isochronous packets. Only used for I/O with isochronous
- * endpoints. */
+ * endpoints. Must be non-negative. */
int num_iso_packets;
/** Isochronous packet descriptors, for isochronous transfers only. */
*/
enum libusb_capability {
/** The libusb_has_capability() API is available. */
- LIBUSB_CAP_HAS_CAPABILITY = 0x0000,
+ LIBUSB_CAP_HAS_CAPABILITY = 0x0000U,
+
/** Hotplug support is available on this platform. */
- LIBUSB_CAP_HAS_HOTPLUG = 0x0001,
+ LIBUSB_CAP_HAS_HOTPLUG = 0x0001U,
+
/** The library can access HID devices without requiring user intervention.
* Note that before being able to actually access an HID device, you may
* still have to call additional libusb functions such as
* \ref libusb_detach_kernel_driver(). */
- LIBUSB_CAP_HAS_HID_ACCESS = 0x0100,
- /** The library supports detaching of the default USB driver, using
+ LIBUSB_CAP_HAS_HID_ACCESS = 0x0100U,
+
+ /** The library supports detaching of the default USB driver, using
* \ref libusb_detach_kernel_driver(), if one is set by the OS kernel */
- LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101
+ LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101U
};
/** \ingroup libusb_lib
* Log message levels.
- * - LIBUSB_LOG_LEVEL_NONE (0) : no messages ever printed by the library (default)
- * - LIBUSB_LOG_LEVEL_ERROR (1) : error messages are printed to stderr
- * - LIBUSB_LOG_LEVEL_WARNING (2) : warning and error messages are printed to stderr
- * - LIBUSB_LOG_LEVEL_INFO (3) : informational messages are printed to stderr
- * - LIBUSB_LOG_LEVEL_DEBUG (4) : debug and informational messages are printed to stderr
*/
enum libusb_log_level {
+ /** (0) : No messages ever emitted by the library (default) */
LIBUSB_LOG_LEVEL_NONE = 0,
+
+ /** (1) : Error messages are emitted */
LIBUSB_LOG_LEVEL_ERROR = 1,
+
+ /** (2) : Warning and error messages are emitted */
LIBUSB_LOG_LEVEL_WARNING = 2,
+
+ /** (3) : Informational, warning and error messages are emitted */
LIBUSB_LOG_LEVEL_INFO = 3,
- LIBUSB_LOG_LEVEL_DEBUG = 4,
+
+ /** (4) : All messages are emitted */
+ LIBUSB_LOG_LEVEL_DEBUG = 4
};
+/** \ingroup libusb_lib
+ * Log callback mode.
+ * \see libusb_set_log_cb()
+ */
+enum libusb_log_cb_mode {
+ /** Callback function handling all log messages. */
+ LIBUSB_LOG_CB_GLOBAL = (1 << 0),
+
+ /** Callback function handling context related log messages. */
+ LIBUSB_LOG_CB_CONTEXT = (1 << 1)
+};
+
+/** \ingroup libusb_lib
+ * Callback function for handling log messages.
+ * \param ctx the context which is related to the log message, or NULL if it
+ * is a global log message
+ * \param level the log level, see \ref libusb_log_level for a description
+ * \param str the log message
+ * \see libusb_set_log_cb()
+ */
+typedef void (LIBUSB_CALL *libusb_log_cb)(libusb_context *ctx,
+ enum libusb_log_level level, const char *str);
+
int LIBUSB_CALL libusb_init(libusb_context **ctx);
void LIBUSB_CALL libusb_exit(libusb_context *ctx);
LIBUSB_DEPRECATED_FOR(libusb_set_option)
void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level);
+void LIBUSB_CALL libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, int mode);
const struct libusb_version * LIBUSB_CALL libusb_get_version(void);
int LIBUSB_CALL libusb_has_capability(uint32_t capability);
const char * LIBUSB_CALL libusb_error_name(int errcode);
int LIBUSB_CALL libusb_setlocale(const char *locale);
-const char * LIBUSB_CALL libusb_strerror(enum libusb_error errcode);
+const char * LIBUSB_CALL libusb_strerror(int errcode);
ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx,
libusb_device ***list);
void LIBUSB_CALL libusb_free_config_descriptor(
struct libusb_config_descriptor *config);
int LIBUSB_CALL libusb_get_ss_endpoint_companion_descriptor(
- struct libusb_context *ctx,
+ libusb_context *ctx,
const struct libusb_endpoint_descriptor *endpoint,
struct libusb_ss_endpoint_companion_descriptor **ep_comp);
void LIBUSB_CALL libusb_free_ss_endpoint_companion_descriptor(
struct libusb_bos_descriptor **bos);
void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos);
int LIBUSB_CALL libusb_get_usb_2_0_extension_descriptor(
- struct libusb_context *ctx,
+ libusb_context *ctx,
struct libusb_bos_dev_capability_descriptor *dev_cap,
struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension);
void LIBUSB_CALL libusb_free_usb_2_0_extension_descriptor(
struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension);
int LIBUSB_CALL libusb_get_ss_usb_device_capability_descriptor(
- struct libusb_context *ctx,
+ libusb_context *ctx,
struct libusb_bos_dev_capability_descriptor *dev_cap,
struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap);
void LIBUSB_CALL libusb_free_ss_usb_device_capability_descriptor(
struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap);
-int LIBUSB_CALL libusb_get_container_id_descriptor(struct libusb_context *ctx,
+int LIBUSB_CALL libusb_get_container_id_descriptor(libusb_context *ctx,
struct libusb_bos_dev_capability_descriptor *dev_cap,
struct libusb_container_id_descriptor **container_id);
void LIBUSB_CALL libusb_free_container_id_descriptor(
struct libusb_container_id_descriptor *container_id);
uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev);
uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev);
-int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t* port_numbers, int port_numbers_len);
+int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t *port_numbers, int port_numbers_len);
LIBUSB_DEPRECATED_FOR(libusb_get_port_numbers)
-int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t* path, uint8_t path_length);
+int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t *path, uint8_t path_length);
libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev);
uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev);
int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev);
int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev,
unsigned char endpoint);
+int LIBUSB_CALL libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, libusb_device_handle **dev_handle);
int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **dev_handle);
void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle);
libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle);
static inline struct libusb_control_setup *libusb_control_transfer_get_setup(
struct libusb_transfer *transfer)
{
- return (struct libusb_control_setup *)(void *) transfer->buffer;
+ return (struct libusb_control_setup *)(void *)transfer->buffer;
}
/** \ingroup libusb_asyncio
uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
uint16_t wLength)
{
- struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer;
+ struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *)buffer;
setup->bmRequestType = bmRequestType;
setup->bRequest = bRequest;
setup->wValue = libusb_cpu_to_le16(wValue);
unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data,
unsigned int timeout)
{
- struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer;
+ struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *)buffer;
transfer->dev_handle = dev_handle;
transfer->endpoint = 0;
transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL;
struct libusb_transfer *transfer, unsigned int length)
{
int i;
+
for (i = 0; i < transfer->num_iso_packets; i++)
transfer->iso_packet_desc[i].length = length;
}
* Callbacks handles are generated by libusb_hotplug_register_callback()
* and can be used to deregister callbacks. Callback handles are unique
* per libusb_context and it is safe to call libusb_hotplug_deregister_callback()
- * on an already deregisted callback.
+ * on an already deregistered callback.
*
* Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
*
*
* Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
*
- * Flags for hotplug events */
+ * Hotplug events */
typedef enum {
- /** Default value when not using any flags. */
- LIBUSB_HOTPLUG_NO_FLAGS = 0,
+ /** A device has been plugged in and is ready to use */
+ LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = (1 << 0),
- /** Arm the callback and fire it for all matching currently attached devices. */
- LIBUSB_HOTPLUG_ENUMERATE = 1<<0,
-} libusb_hotplug_flag;
+ /** A device has left and is no longer available.
+ * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device.
+ * It is safe to call libusb_get_device_descriptor on a device that has left */
+ LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = (1 << 1)
+} libusb_hotplug_event;
/** \ingroup libusb_hotplug
*
* Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
*
- * Hotplug events */
+ * Hotplug flags */
typedef enum {
- /** A device has been plugged in and is ready to use */
- LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = 0x01,
+ /** Arm the callback and fire it for all matching currently attached devices. */
+ LIBUSB_HOTPLUG_ENUMERATE = (1 << 0)
+} libusb_hotplug_flag;
- /** A device has left and is no longer available.
- * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device.
- * It is safe to call libusb_get_device_descriptor on a device that has left */
- LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = 0x02,
-} libusb_hotplug_event;
+/** \ingroup libusb_hotplug
+ * Convenience macro when not using any flags */
+#define LIBUSB_HOTPLUG_NO_FLAGS 0
/** \ingroup libusb_hotplug
* Wildcard matching for hotplug events */
* returning 1 will cause this callback to be deregistered
*/
typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx,
- libusb_device *device,
- libusb_hotplug_event event,
- void *user_data);
+ libusb_device *device, libusb_hotplug_event event, void *user_data);
/** \ingroup libusb_hotplug
* Register a hotplug callback function
* Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
*
* \param[in] ctx context to register this callback with
- * \param[in] events bitwise or of events that will trigger this callback. See \ref
- * libusb_hotplug_event
- * \param[in] flags hotplug callback flags. See \ref libusb_hotplug_flag
+ * \param[in] events bitwise or of hotplug events that will trigger this callback.
+ * See \ref libusb_hotplug_event
+ * \param[in] flags bitwise or of hotplug flags that affect registration.
+ * See \ref libusb_hotplug_flag
* \param[in] vendor_id the vendor id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
* \param[in] product_id the product id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
* \param[in] dev_class the device class to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
* \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure
*/
int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx,
- libusb_hotplug_event events,
- libusb_hotplug_flag flags,
- int vendor_id, int product_id,
- int dev_class,
- libusb_hotplug_callback_fn cb_fn,
- void *user_data,
- libusb_hotplug_callback_handle *callback_handle);
+ int events, int flags,
+ int vendor_id, int product_id, int dev_class,
+ libusb_hotplug_callback_fn cb_fn, void *user_data,
+ libusb_hotplug_callback_handle *callback_handle);
/** \ingroup libusb_hotplug
* Deregisters a hotplug callback.
* \param[in] callback_handle the handle of the callback to deregister
*/
void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx,
- libusb_hotplug_callback_handle callback_handle);
+ libusb_hotplug_callback_handle callback_handle);
+
+/** \ingroup libusb_hotplug
+ * Gets the user_data associated with a hotplug callback.
+ *
+ * Since version v1.0.24 \ref LIBUSB_API_VERSION >= 0x01000108
+ *
+ * \param[in] ctx context this callback is registered with
+ * \param[in] callback_handle the handle of the callback to get the user_data of
+ */
+void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx,
+ libusb_hotplug_callback_handle callback_handle);
/** \ingroup libusb_lib
* Available option values for libusb_set_option().
* If libusb was compiled with verbose debug message logging, this function
* does nothing: you'll always get messages from all levels.
*/
- LIBUSB_OPTION_LOG_LEVEL,
+ LIBUSB_OPTION_LOG_LEVEL = 0,
/** Use the UsbDk backend for a specific context, if available.
*
*
* Only valid on Windows.
*/
- LIBUSB_OPTION_USE_USBDK,
+ LIBUSB_OPTION_USE_USBDK = 1,
+
+ /** Set libusb has weak authority. With this option, libusb will skip
+ * scan devices in libusb_init.
+ *
+ * This option should be set before calling libusb_init(), otherwise
+ * libusb_init will failed. Normally libusb_wrap_sys_device need set
+ * this option.
+ *
+ * Only valid on Linux-based operating system, such as Android.
+ */
+ LIBUSB_OPTION_WEAK_AUTHORITY = 2
};
int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...);
-#ifdef __cplusplus
+#if defined(__cplusplus)
}
#endif
strerror.c \
sync.c \
os/darwin_usb.c \
- os/poll_posix.c \
+ os/events_posix.c \
os/threads_posix.c
HEADERS = hotplug.h \
version.h \
version_nano.h \
os/darwin_usb.h \
- os/poll_posix.h \
+ os/events_posix.h \
os/threads_posix.h
# We use libusb-1.0.0's hardcoded config.h for Xcode
* Internal header for libusb
* Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org>
* Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
+ * Copyright © 2019 Nathan Hjelm <hjelmn@cs.umm.edu>
+ * Copyright © 2019-2020 Google LLC. All rights reserved.
+ * Copyright © 2020 Chris Dickens <christopher.a.dickens@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include <config.h>
-#include <stdlib.h>
-
-#include <stddef.h>
-#include <stdint.h>
-#include <time.h>
+#include <assert.h>
+#include <inttypes.h>
#include <stdarg.h>
-#ifdef HAVE_POLL_H
-#include <poll.h>
-#endif
-#ifdef HAVE_MISSING_H
-#include <missing.h>
+#include <stddef.h>
+#include <stdlib.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
#endif
#include "libusb.h"
-#include "version.h"
-/* Attribute to ensure that a structure member is aligned to a natural
- * pointer alignment. Used for os_priv member. */
-#if defined(_MSC_VER)
-#if defined(_WIN64)
-#define PTR_ALIGNED __declspec(align(8))
+/* Not all C standard library headers define static_assert in assert.h
+ * Additionally, Visual Studio treats static_assert as a keyword.
+ */
+#if !defined(__cplusplus) && !defined(static_assert) && !defined(_MSC_VER)
+#define static_assert(cond, msg) _Static_assert(cond, msg)
+#endif
+
+#ifdef NDEBUG
+#define ASSERT_EQ(expression, value) (void)expression
+#define ASSERT_NE(expression, value) (void)expression
#else
-#define PTR_ALIGNED __declspec(align(4))
+#define ASSERT_EQ(expression, value) assert(expression == value)
+#define ASSERT_NE(expression, value) assert(expression != value)
+#endif
+
+#define container_of(ptr, type, member) \
+ ((type *)((uintptr_t)(ptr) - (uintptr_t)offsetof(type, member)))
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(array) (sizeof(array) / sizeof(array[0]))
#endif
-#elif defined(__GNUC__)
-#define PTR_ALIGNED __attribute__((aligned(sizeof(void *))))
+
+#ifndef CLAMP
+#define CLAMP(val, min, max) \
+ ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val)))
+#endif
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+/* The following is used to silence warnings for unused variables */
+#if defined(UNREFERENCED_PARAMETER)
+#define UNUSED(var) UNREFERENCED_PARAMETER(var)
#else
-#define PTR_ALIGNED
+#define UNUSED(var) do { (void)(var); } while(0)
+#endif
+
+/* Macro to align a value up to the next multiple of the size of a pointer */
+#define PTR_ALIGN(v) \
+ (((v) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1))
+
+/* Internal abstractions for event handling and thread synchronization */
+#if defined(PLATFORM_POSIX)
+#include "os/events_posix.h"
+#include "os/threads_posix.h"
+#elif defined(PLATFORM_WINDOWS)
+#include "os/events_windows.h"
+#include "os/threads_windows.h"
#endif
/* Inside the libusb code, mark all public functions as follows:
extern "C" {
#endif
-#define DEVICE_DESC_LENGTH 18
-
#define USB_MAXENDPOINTS 32
#define USB_MAXINTERFACES 32
#define USB_MAXCONFIG 8
/* Terminator for log lines */
#define USBI_LOG_LINE_END "\n"
-/* The following is used to silence warnings for unused variables */
-#define UNUSED(var) do { (void)(var); } while(0)
-
-#if !defined(ARRAYSIZE)
-#define ARRAYSIZE(array) (sizeof(array) / sizeof(array[0]))
-#endif
-
struct list_head {
struct list_head *prev, *next;
};
* member - the list_head element in "type"
*/
#define list_entry(ptr, type, member) \
- ((type *)((uintptr_t)(ptr) - (uintptr_t)offsetof(type, member)))
+ container_of(ptr, type, member)
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
+#define list_next_entry(ptr, type, member) \
+ list_entry((ptr)->member.next, type, member)
+
/* Get each entry from a list
* pos - A structure pointer has a "member" element
* head - list head
* type - the type of the first parameter
*/
#define list_for_each_entry(pos, head, member, type) \
- for (pos = list_entry((head)->next, type, member); \
+ for (pos = list_first_entry(head, type, member); \
&pos->member != (head); \
- pos = list_entry(pos->member.next, type, member))
+ pos = list_next_entry(pos, type, member))
#define list_for_each_entry_safe(pos, n, head, member, type) \
- for (pos = list_entry((head)->next, type, member), \
- n = list_entry(pos->member.next, type, member); \
+ for (pos = list_first_entry(head, type, member), \
+ n = list_next_entry(pos, type, member); \
&pos->member != (head); \
- pos = n, n = list_entry(n->member.next, type, member))
+ pos = n, n = list_next_entry(n, type, member))
+
+/* Helper macros to iterate over a list. The structure pointed
+ * to by "pos" must have a list_head member named "list".
+ */
+#define for_each_helper(pos, head, type) \
+ list_for_each_entry(pos, head, list, type)
+
+#define for_each_safe_helper(pos, n, head, type) \
+ list_for_each_entry_safe(pos, n, head, list, type)
#define list_empty(entry) ((entry)->next == (entry))
static inline void list_cut(struct list_head *list, struct list_head *head)
{
- if (list_empty(head))
+ if (list_empty(head)) {
+ list_init(list);
return;
+ }
list->next = head->next;
list->next->prev = list;
list_init(head);
}
+static inline void list_splice_front(struct list_head *list, struct list_head *head)
+{
+ list->next->prev = head;
+ list->prev->next = head->next;
+ head->next->prev = list->prev;
+ head->next = list->next;
+}
+
static inline void *usbi_reallocf(void *ptr, size_t size)
{
void *ret = realloc(ptr, size);
+
if (!ret)
free(ptr);
return ret;
}
-#define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *mptr = (ptr); \
- (type *)( (char *)mptr - offsetof(type,member) );})
-
-#ifndef CLAMP
-#define CLAMP(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val)))
-#endif
-#ifndef MIN
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#if !defined(USEC_PER_SEC)
+#define USEC_PER_SEC 1000000L
#endif
-#ifndef MAX
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#if !defined(NSEC_PER_SEC)
+#define NSEC_PER_SEC 1000000000L
#endif
-#define TIMESPEC_IS_SET(ts) ((ts)->tv_sec != 0 || (ts)->tv_nsec != 0)
+#define TIMEVAL_IS_VALID(tv) \
+ ((tv)->tv_sec >= 0 && \
+ (tv)->tv_usec >= 0 && (tv)->tv_usec < USEC_PER_SEC)
+
+#define TIMESPEC_IS_SET(ts) ((ts)->tv_sec || (ts)->tv_nsec)
+#define TIMESPEC_CLEAR(ts) (ts)->tv_sec = (ts)->tv_nsec = 0
+#define TIMESPEC_CMP(a, b, CMP) \
+ (((a)->tv_sec == (b)->tv_sec) \
+ ? ((a)->tv_nsec CMP (b)->tv_nsec) \
+ : ((a)->tv_sec CMP (b)->tv_sec))
+#define TIMESPEC_SUB(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \
+ if ((result)->tv_nsec < 0L) { \
+ --(result)->tv_sec; \
+ (result)->tv_nsec += NSEC_PER_SEC; \
+ } \
+ } while (0)
-#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+#if defined(PLATFORM_WINDOWS)
#define TIMEVAL_TV_SEC_TYPE long
#else
#define TIMEVAL_TV_SEC_TYPE time_t
#define TIMESPEC_TO_TIMEVAL(tv, ts) \
do { \
(tv)->tv_sec = (TIMEVAL_TV_SEC_TYPE) (ts)->tv_sec; \
- (tv)->tv_usec = (ts)->tv_nsec / 1000; \
+ (tv)->tv_usec = (ts)->tv_nsec / 1000L; \
} while (0)
#endif
#ifdef ENABLE_LOGGING
#if defined(_MSC_VER) && (_MSC_VER < 1900)
+#include <stdio.h>
#define snprintf usbi_snprintf
#define vsnprintf usbi_vsnprintf
int usbi_snprintf(char *dst, size_t size, const char *format, ...);
-int usbi_vsnprintf(char *dst, size_t size, const char *format, va_list ap);
+int usbi_vsnprintf(char *dst, size_t size, const char *format, va_list args);
#define LIBUSB_PRINTF_WIN32
#endif /* defined(_MSC_VER) && (_MSC_VER < 1900) */
void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
- const char *function, const char *format, ...);
-
-void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
- const char *function, const char *format, va_list args);
-
-#if !defined(_MSC_VER) || (_MSC_VER >= 1400)
-
-#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __FUNCTION__, __VA_ARGS__)
+ const char *function, const char *format, ...) PRINTF_FORMAT(4, 5);
-#define usbi_err(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_ERROR, __VA_ARGS__)
-#define usbi_warn(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__)
-#define usbi_info(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__)
-#define usbi_dbg(...) _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
+#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __func__, __VA_ARGS__)
-#else /* !defined(_MSC_VER) || (_MSC_VER >= 1400) */
-
-#define LOG_BODY(ctxt, level) \
-{ \
- va_list args; \
- va_start(args, format); \
- usbi_log_v(ctxt, level, "", format, args); \
- va_end(args); \
-}
-
-static inline void usbi_err(struct libusb_context *ctx, const char *format, ...)
- LOG_BODY(ctx, LIBUSB_LOG_LEVEL_ERROR)
-static inline void usbi_warn(struct libusb_context *ctx, const char *format, ...)
- LOG_BODY(ctx, LIBUSB_LOG_LEVEL_WARNING)
-static inline void usbi_info(struct libusb_context *ctx, const char *format, ...)
- LOG_BODY(ctx, LIBUSB_LOG_LEVEL_INFO)
-static inline void usbi_dbg(const char *format, ...)
- LOG_BODY(NULL, LIBUSB_LOG_LEVEL_DEBUG)
-
-#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1400) */
+#define usbi_err(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_ERROR, __VA_ARGS__)
+#define usbi_warn(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__)
+#define usbi_info(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__)
+#define usbi_dbg(...) _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
#else /* ENABLE_LOGGING */
-#define usbi_err(ctx, ...) do { (void)ctx; } while (0)
-#define usbi_warn(ctx, ...) do { (void)ctx; } while (0)
-#define usbi_info(ctx, ...) do { (void)ctx; } while (0)
-#define usbi_dbg(...) do {} while (0)
+#define usbi_err(ctx, ...) UNUSED(ctx)
+#define usbi_warn(ctx, ...) UNUSED(ctx)
+#define usbi_info(ctx, ...) UNUSED(ctx)
+#define usbi_dbg(...) do {} while (0)
#endif /* ENABLE_LOGGING */
-#define USBI_GET_CONTEXT(ctx) \
- do { \
- if (!(ctx)) \
- (ctx) = usbi_default_context; \
- } while(0)
-
#define DEVICE_CTX(dev) ((dev)->ctx)
#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev))
#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle))
-#define ITRANSFER_CTX(transfer) \
- (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)))
+#define ITRANSFER_CTX(itransfer) \
+ (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)))
#define IS_EPIN(ep) (0 != ((ep) & LIBUSB_ENDPOINT_IN))
#define IS_EPOUT(ep) (!IS_EPIN(ep))
#define IS_XFERIN(xfer) (0 != ((xfer)->endpoint & LIBUSB_ENDPOINT_IN))
#define IS_XFEROUT(xfer) (!IS_XFERIN(xfer))
-/* Internal abstraction for thread synchronization */
-#if defined(THREADS_POSIX)
-#include "os/threads_posix.h"
-#elif defined(OS_WINDOWS) || defined(OS_WINCE)
-#include "os/threads_windows.h"
-#endif
-
-extern struct libusb_context *usbi_default_context;
-
-/* Forward declaration for use in context (fully defined inside poll abstraction) */
-struct pollfd;
-
struct libusb_context {
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
enum libusb_log_level debug;
int debug_fixed;
+ libusb_log_cb log_handler;
#endif
- /* internal event pipe, used for signalling occurrence of an internal event. */
- int event_pipe[2];
+ /* used for signalling occurrence of an internal event. */
+ usbi_event_t event;
+
+#ifdef HAVE_OS_TIMER
+ /* used for timeout handling, if supported by OS.
+ * this timer is maintained to trigger on the next pending timeout */
+ usbi_timer_t timer;
+#endif
struct list_head usb_devs;
usbi_mutex_t usb_devs_lock;
* take this lock first */
usbi_mutex_t flying_transfers_lock;
+#if !defined(PLATFORM_WINDOWS)
/* user callbacks for pollfd changes */
libusb_pollfd_added_cb fd_added_cb;
libusb_pollfd_removed_cb fd_removed_cb;
void *fd_cb_user_data;
+#endif
/* ensures that only one thread is handling events at any one time */
usbi_mutex_t events_lock;
* in order to safely close a device. Protected by event_data_lock. */
unsigned int device_close;
- /* list and count of poll fds and an array of poll fd structures that is
- * (re)allocated as necessary prior to polling. Protected by event_data_lock. */
- struct list_head ipollfds;
- struct pollfd *pollfds;
- POLL_NFDS_TYPE pollfds_cnt;
+ /* A list of currently active event sources. Protected by event_data_lock. */
+ struct list_head event_sources;
+
+ /* A list of event sources that have been removed since the last time
+ * event sources were waited on. Protected by event_data_lock. */
+ struct list_head removed_event_sources;
+
+ /* A pointer and count to platform-specific data used for monitoring event
+ * sources. Only accessed during event handling. */
+ void *event_data;
+ unsigned int event_data_cnt;
/* A list of pending hotplug messages. Protected by event_data_lock. */
struct list_head hotplug_msgs;
/* A list of pending completed transfers. Protected by event_data_lock. */
struct list_head completed_transfers;
-#ifdef USBI_TIMERFD_AVAILABLE
- /* used for timeout handling, if supported by OS.
- * this timerfd is maintained to trigger on the next pending timeout */
- int timerfd;
-#endif
-
struct list_head list;
-
- PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
};
+extern struct libusb_context *usbi_default_context;
+
+extern struct list_head active_contexts_list;
+extern usbi_mutex_static_t active_contexts_lock;
+
+static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx)
+{
+ return ctx ? ctx : usbi_default_context;
+}
+
enum usbi_event_flags {
- /* The list of pollfds has been modified */
- USBI_EVENT_POLLFDS_MODIFIED = 1 << 0,
+ /* The list of event sources has been modified */
+ USBI_EVENT_EVENT_SOURCES_MODIFIED = 1U << 0,
/* The user has interrupted the event handler */
- USBI_EVENT_USER_INTERRUPT = 1 << 1,
+ USBI_EVENT_USER_INTERRUPT = 1U << 1,
/* A hotplug callback deregistration is pending */
- USBI_EVENT_HOTPLUG_CB_DEREGISTERED = 1 << 2,
-};
+ USBI_EVENT_HOTPLUG_CB_DEREGISTERED = 1U << 2,
-/* Macros for managing event handling state */
-#define usbi_handling_events(ctx) \
- (usbi_tls_key_get((ctx)->event_handling_key) != NULL)
+ /* One or more hotplug messages are pending */
+ USBI_EVENT_HOTPLUG_MSG_PENDING = 1U << 3,
-#define usbi_start_event_handling(ctx) \
- usbi_tls_key_set((ctx)->event_handling_key, ctx)
+ /* One or more completed transfers are pending */
+ USBI_EVENT_TRANSFER_COMPLETED = 1U << 4,
-#define usbi_end_event_handling(ctx) \
- usbi_tls_key_set((ctx)->event_handling_key, NULL)
+ /* A device is in the process of being closed */
+ USBI_EVENT_DEVICE_CLOSE = 1U << 5,
+};
-/* Update the following macro if new event sources are added */
-#define usbi_pending_events(ctx) \
- ((ctx)->event_flags || (ctx)->device_close \
- || !list_empty(&(ctx)->hotplug_msgs) || !list_empty(&(ctx)->completed_transfers))
+/* Macros for managing event handling state */
+static inline int usbi_handling_events(struct libusb_context *ctx)
+{
+ return usbi_tls_key_get(ctx->event_handling_key) != NULL;
+}
-#ifdef USBI_TIMERFD_AVAILABLE
-#define usbi_using_timerfd(ctx) ((ctx)->timerfd >= 0)
-#else
-#define usbi_using_timerfd(ctx) (0)
-#endif
+static inline void usbi_start_event_handling(struct libusb_context *ctx)
+{
+ usbi_tls_key_set(ctx->event_handling_key, ctx);
+}
+
+static inline void usbi_end_event_handling(struct libusb_context *ctx)
+{
+ usbi_tls_key_set(ctx->event_handling_key, NULL);
+}
struct libusb_device {
/* lock protects refcnt, everything else is finalized at initialization
int refcnt;
struct libusb_context *ctx;
+ struct libusb_device *parent_dev;
uint8_t bus_number;
uint8_t port_number;
- struct libusb_device* parent_dev;
uint8_t device_address;
- uint8_t num_configurations;
enum libusb_speed speed;
struct list_head list;
struct libusb_device_descriptor device_descriptor;
int attached;
-
- PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
};
struct libusb_device_handle {
struct list_head list;
struct libusb_device *dev;
int auto_detach_kernel_driver;
-
- PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
};
-enum {
- USBI_CLOCK_MONOTONIC,
- USBI_CLOCK_REALTIME
-};
+/* Function called by backend during device initialization to convert
+ * multi-byte fields in the device descriptor to host-endian format.
+ */
+static inline void usbi_localize_device_descriptor(struct libusb_device_descriptor *desc)
+{
+ desc->bcdUSB = libusb_le16_to_cpu(desc->bcdUSB);
+ desc->idVendor = libusb_le16_to_cpu(desc->idVendor);
+ desc->idProduct = libusb_le16_to_cpu(desc->idProduct);
+ desc->bcdDevice = libusb_le16_to_cpu(desc->bcdDevice);
+}
+
+#ifdef HAVE_CLOCK_GETTIME
+static inline void usbi_get_monotonic_time(struct timespec *tp)
+{
+ ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, tp), 0);
+}
+static inline void usbi_get_real_time(struct timespec *tp)
+{
+ ASSERT_EQ(clock_gettime(CLOCK_REALTIME, tp), 0);
+}
+#else
+/* If the platform doesn't provide the clock_gettime() function, the backend
+ * must provide its own clock implementations. Two clock functions are
+ * required:
+ *
+ * usbi_get_monotonic_time(): returns the time since an unspecified starting
+ * point (usually boot) that is monotonically
+ * increasing.
+ * usbi_get_real_time(): returns the time since system epoch.
+ */
+void usbi_get_monotonic_time(struct timespec *tp);
+void usbi_get_real_time(struct timespec *tp);
+#endif
/* in-memory transfer layout:
*
- * 1. struct usbi_transfer
- * 2. struct libusb_transfer (which includes iso packets) [variable size]
- * 3. os private data [variable size]
+ * 1. os private data
+ * 2. struct usbi_transfer
+ * 3. struct libusb_transfer (which includes iso packets) [variable size]
*
* from a libusb_transfer, you can get the usbi_transfer by rewinding the
* appropriate number of bytes.
- * the usbi_transfer includes the number of allocated packets, so you can
- * determine the size of the transfer and hence the start and length of the
- * OS-private data.
*/
struct usbi_transfer {
int num_iso_packets;
struct list_head list;
struct list_head completed_list;
- struct timeval timeout;
+ struct timespec timeout;
int transferred;
uint32_t stream_id;
- uint8_t state_flags; /* Protected by usbi_transfer->lock */
- uint8_t timeout_flags; /* Protected by the flying_stransfers_lock */
+ uint32_t state_flags; /* Protected by usbi_transfer->lock */
+ uint32_t timeout_flags; /* Protected by the flying_stransfers_lock */
/* this lock is held during libusb_submit_transfer() and
* libusb_cancel_transfer() (allowing the OS backend to prevent duplicate
* Note paths taking both this and the flying_transfers_lock must
* always take the flying_transfers_lock first */
usbi_mutex_t lock;
+
+ void *priv;
};
enum usbi_transfer_state_flags {
/* Transfer successfully submitted by backend */
- USBI_TRANSFER_IN_FLIGHT = 1 << 0,
+ USBI_TRANSFER_IN_FLIGHT = 1U << 0,
/* Cancellation was requested via libusb_cancel_transfer() */
- USBI_TRANSFER_CANCELLING = 1 << 1,
+ USBI_TRANSFER_CANCELLING = 1U << 1,
/* Operation on the transfer failed because the device disappeared */
- USBI_TRANSFER_DEVICE_DISAPPEARED = 1 << 2,
+ USBI_TRANSFER_DEVICE_DISAPPEARED = 1U << 2,
};
enum usbi_transfer_timeout_flags {
/* Set by backend submit_transfer() if the OS handles timeout */
- USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1 << 0,
+ USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1U << 0,
/* The transfer timeout has been handled */
- USBI_TRANSFER_TIMEOUT_HANDLED = 1 << 1,
+ USBI_TRANSFER_TIMEOUT_HANDLED = 1U << 1,
/* The transfer timeout was successfully processed */
- USBI_TRANSFER_TIMED_OUT = 1 << 2,
+ USBI_TRANSFER_TIMED_OUT = 1U << 2,
};
-#define USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \
- ((struct libusb_transfer *)(((unsigned char *)(transfer)) \
- + sizeof(struct usbi_transfer)))
-#define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \
- ((struct usbi_transfer *)(((unsigned char *)(transfer)) \
- - sizeof(struct usbi_transfer)))
+#define USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer) \
+ ((struct libusb_transfer *) \
+ ((unsigned char *)(itransfer) \
+ + PTR_ALIGN(sizeof(struct usbi_transfer))))
+#define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \
+ ((struct usbi_transfer *) \
+ ((unsigned char *)(transfer) \
+ - PTR_ALIGN(sizeof(struct usbi_transfer))))
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif
-static inline void *usbi_transfer_get_os_priv(struct usbi_transfer *transfer)
-{
- return ((unsigned char *)transfer) + sizeof(struct usbi_transfer)
- + sizeof(struct libusb_transfer)
- + (transfer->num_iso_packets
- * sizeof(struct libusb_iso_packet_descriptor));
-}
+/* All standard descriptors have these 2 fields in common */
+struct usbi_descriptor_header {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+} LIBUSB_PACKED;
+
+struct usbi_device_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t bcdUSB;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bMaxPacketSize0;
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+ uint8_t iManufacturer;
+ uint8_t iProduct;
+ uint8_t iSerialNumber;
+ uint8_t bNumConfigurations;
+} LIBUSB_PACKED;
+
+struct usbi_configuration_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t wTotalLength;
+ uint8_t bNumInterfaces;
+ uint8_t bConfigurationValue;
+ uint8_t iConfiguration;
+ uint8_t bmAttributes;
+ uint8_t bMaxPower;
+} LIBUSB_PACKED;
+
+struct usbi_interface_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bInterfaceNumber;
+ uint8_t bAlternateSetting;
+ uint8_t bNumEndpoints;
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t iInterface;
+} LIBUSB_PACKED;
+
+struct usbi_string_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t wData[ZERO_SIZED_ARRAY];
+} LIBUSB_PACKED;
+
+struct usbi_bos_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t wTotalLength;
+ uint8_t bNumDeviceCaps;
+} LIBUSB_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif
-/* bus structures */
+union usbi_config_desc_buf {
+ struct usbi_configuration_descriptor desc;
+ uint8_t buf[LIBUSB_DT_CONFIG_SIZE];
+ uint16_t align; /* Force 2-byte alignment */
+};
-/* All standard descriptors have these 2 fields in common */
-struct usb_descriptor_header {
- uint8_t bLength;
- uint8_t bDescriptorType;
+union usbi_string_desc_buf {
+ struct usbi_string_descriptor desc;
+ uint8_t buf[255]; /* Some devices choke on size > 255 */
+ uint16_t align; /* Force 2-byte alignment */
+};
+
+union usbi_bos_desc_buf {
+ struct usbi_bos_descriptor desc;
+ uint8_t buf[LIBUSB_DT_BOS_SIZE];
+ uint16_t align; /* Force 2-byte alignment */
};
/* shared data and functions */
int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
enum libusb_transfer_status status);
-int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer);
-void usbi_signal_transfer_completion(struct usbi_transfer *transfer);
-
-int usbi_parse_descriptor(const unsigned char *source, const char *descriptor,
- void *dest, int host_endian);
-int usbi_device_cache_descriptor(libusb_device *dev);
-int usbi_get_config_index_by_value(struct libusb_device *dev,
- uint8_t bConfigurationValue, int *idx);
-
-void usbi_connect_device (struct libusb_device *dev);
-void usbi_disconnect_device (struct libusb_device *dev);
-
-int usbi_signal_event(struct libusb_context *ctx);
-int usbi_clear_event(struct libusb_context *ctx);
-
-/* Internal abstraction for poll (needs struct usbi_transfer on Windows) */
-#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD) || defined(OS_NETBSD) ||\
- defined(OS_HAIKU) || defined(OS_SUNOS)
-#include <unistd.h>
-#include "os/poll_posix.h"
-#elif defined(OS_WINDOWS) || defined(OS_WINCE)
-#include "os/poll_windows.h"
-#endif
+int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer);
+void usbi_signal_transfer_completion(struct usbi_transfer *itransfer);
-struct usbi_pollfd {
- /* must come first */
- struct libusb_pollfd pollfd;
+void usbi_connect_device(struct libusb_device *dev);
+void usbi_disconnect_device(struct libusb_device *dev);
+struct usbi_event_source {
+ struct usbi_event_source_data {
+ usbi_os_handle_t os_handle;
+ short poll_events;
+ } data;
struct list_head list;
};
-int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events);
-void usbi_remove_pollfd(struct libusb_context *ctx, int fd);
+int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle,
+ short poll_events);
+void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle);
+
+/* OS event abstraction */
+
+int usbi_create_event(usbi_event_t *event);
+void usbi_destroy_event(usbi_event_t *event);
+void usbi_signal_event(usbi_event_t *event);
+void usbi_clear_event(usbi_event_t *event);
+
+#ifdef HAVE_OS_TIMER
+int usbi_create_timer(usbi_timer_t *timer);
+void usbi_destroy_timer(usbi_timer_t *timer);
+int usbi_arm_timer(usbi_timer_t *timer, const struct timespec *timeout);
+int usbi_disarm_timer(usbi_timer_t *timer);
+#endif
+
+static inline int usbi_using_timer(struct libusb_context *ctx)
+{
+#ifdef HAVE_OS_TIMER
+ return usbi_timer_valid(&ctx->timer);
+#else
+ UNUSED(ctx);
+ return 0;
+#endif
+}
+
+struct usbi_reported_events {
+ union {
+ struct {
+ unsigned int event_triggered:1;
+#ifdef HAVE_OS_TIMER
+ unsigned int timer_triggered:1;
+#endif
+ };
+ unsigned int event_bits;
+ };
+ void *event_data;
+ unsigned int event_data_count;
+ unsigned int num_ready;
+};
+
+int usbi_alloc_event_data(struct libusb_context *ctx);
+int usbi_wait_for_events(struct libusb_context *ctx,
+ struct usbi_reported_events *reported_events, int timeout_ms);
+
+/* accessor functions for structure private data */
+
+static inline void *usbi_get_context_priv(struct libusb_context *ctx)
+{
+ return (unsigned char *)ctx + PTR_ALIGN(sizeof(*ctx));
+}
+
+static inline void *usbi_get_device_priv(struct libusb_device *dev)
+{
+ return (unsigned char *)dev + PTR_ALIGN(sizeof(*dev));
+}
+
+static inline void *usbi_get_device_handle_priv(struct libusb_device_handle *dev_handle)
+{
+ return (unsigned char *)dev_handle + PTR_ALIGN(sizeof(*dev_handle));
+}
+
+static inline void *usbi_get_transfer_priv(struct usbi_transfer *itransfer)
+{
+ return itransfer->priv;
+}
/* device discovery */
*/
void (*hotplug_poll)(void);
+ /* Wrap a platform-specific device handle for I/O and other USB
+ * operations. The device handle is preallocated for you.
+ *
+ * Your backend should allocate any internal resources required for I/O
+ * and other operations so that those operations can happen (hopefully)
+ * without hiccup. This is also a good place to inform libusb that it
+ * should monitor certain file descriptors related to this device -
+ * see the usbi_add_event_source() function.
+ *
+ * Your backend should also initialize the device structure
+ * (dev_handle->dev), which is NULL at the beginning of the call.
+ *
+ * This function should not generate any bus I/O and should not block.
+ *
+ * This function is called when the user attempts to wrap an existing
+ * platform-specific device handle for a device.
+ *
+ * Return:
+ * - 0 on success
+ * - LIBUSB_ERROR_ACCESS if the user has insufficient permissions
+ * - another LIBUSB_ERROR code on other failure
+ *
+ * Do not worry about freeing the handle on failed open, the upper layers
+ * do this for you.
+ */
+ int (*wrap_sys_device)(struct libusb_context *ctx,
+ struct libusb_device_handle *dev_handle, intptr_t sys_dev);
+
/* Open a device for I/O and other USB operations. The device handle
* is preallocated for you, you can retrieve the device in question
* through handle->dev.
* and other operations so that those operations can happen (hopefully)
* without hiccup. This is also a good place to inform libusb that it
* should monitor certain file descriptors related to this device -
- * see the usbi_add_pollfd() function.
+ * see the usbi_add_event_source() function.
*
* This function should not generate any bus I/O and should not block.
*
/* Close a device such that the handle cannot be used again. Your backend
* should destroy any resources that were allocated in the open path.
- * This may also be a good place to call usbi_remove_pollfd() to inform
- * libusb of any file descriptors associated with this device that should
- * no longer be monitored.
+ * This may also be a good place to call usbi_remove_event_source() to
+ * inform libusb of any event sources associated with this device that
+ * should no longer be monitored.
*
* This function is called when the user closes a device handle.
*/
void (*close)(struct libusb_device_handle *dev_handle);
- /* Retrieve the device descriptor from a device.
- *
- * The descriptor should be retrieved from memory, NOT via bus I/O to the
- * device. This means that you may have to cache it in a private structure
- * during get_device_list enumeration. Alternatively, you may be able
- * to retrieve it from a kernel interface (some Linux setups can do this)
- * still without generating bus I/O.
- *
- * This function is expected to write DEVICE_DESC_LENGTH (18) bytes into
- * buffer, which is guaranteed to be big enough.
- *
- * This function is called when sanity-checking a device before adding
- * it to the list of discovered devices, and also when the user requests
- * to read the device descriptor.
- *
- * This function is expected to return the descriptor in bus-endian format
- * (LE). If it returns the multi-byte values in host-endian format,
- * set the host_endian output parameter to "1".
- *
- * Return 0 on success or a LIBUSB_ERROR code on failure.
- */
- int (*get_device_descriptor)(struct libusb_device *device,
- unsigned char *buffer, int *host_endian);
-
/* Get the ACTIVE configuration descriptor for a device.
*
* The descriptor should be retrieved from memory, NOT via bus I/O to the
* return an error code.
*
* This function is expected to return the descriptor in bus-endian format
- * (LE). If it returns the multi-byte values in host-endian format,
- * set the host_endian output parameter to "1".
+ * (LE).
*
* Return:
* - 0 on success
* - another LIBUSB_ERROR code on other failure
*/
int (*get_active_config_descriptor)(struct libusb_device *device,
- unsigned char *buffer, size_t len, int *host_endian);
+ void *buffer, size_t len);
/* Get a specific configuration descriptor for a device.
*
* return an error code.
*
* This function is expected to return the descriptor in bus-endian format
- * (LE). If it returns the multi-byte values in host-endian format,
- * set the host_endian output parameter to "1".
+ * (LE).
*
* Return the length read on success or a LIBUSB_ERROR code on failure.
*/
int (*get_config_descriptor)(struct libusb_device *device,
- uint8_t config_index, unsigned char *buffer, size_t len,
- int *host_endian);
+ uint8_t config_index, void *buffer, size_t len);
/* Like get_config_descriptor but then by bConfigurationValue instead
* of by index.
* or a LIBUSB_ERROR code on failure.
*/
int (*get_config_descriptor_by_value)(struct libusb_device *device,
- uint8_t bConfigurationValue, unsigned char **buffer,
- int *host_endian);
+ uint8_t bConfigurationValue, void **buffer);
/* Get the bConfigurationValue for the active configuration for a device.
* Optional. This should only be implemented if you can retrieve it from
* blocking
* - another LIBUSB_ERROR code on other failure.
*/
- int (*get_configuration)(struct libusb_device_handle *dev_handle, int *config);
+ int (*get_configuration)(struct libusb_device_handle *dev_handle, uint8_t *config);
/* Set the active configuration for a device.
*
* was opened
* - another LIBUSB_ERROR code on other failure
*/
- int (*claim_interface)(struct libusb_device_handle *dev_handle, int interface_number);
+ int (*claim_interface)(struct libusb_device_handle *dev_handle, uint8_t interface_number);
/* Release a previously claimed interface.
*
* was opened
* - another LIBUSB_ERROR code on other failure
*/
- int (*release_interface)(struct libusb_device_handle *dev_handle, int interface_number);
+ int (*release_interface)(struct libusb_device_handle *dev_handle, uint8_t interface_number);
/* Set the alternate setting for an interface.
*
* - another LIBUSB_ERROR code on other failure
*/
int (*set_interface_altsetting)(struct libusb_device_handle *dev_handle,
- int interface_number, int altsetting);
+ uint8_t interface_number, uint8_t altsetting);
/* Clear a halt/stall condition on an endpoint.
*
int (*clear_halt)(struct libusb_device_handle *dev_handle,
unsigned char endpoint);
- /* Perform a USB port reset to reinitialize a device.
+ /* Perform a USB port reset to reinitialize a device. Optional.
*
* If possible, the device handle should still be usable after the reset
* completes, assuming that the device descriptors did not change during
* reset and all previous interface state can be restored.
*
- * If something changes, or you cannot easily locate/verify the resetted
+ * If something changes, or you cannot easily locate/verify the reset
* device, return LIBUSB_ERROR_NOT_FOUND. This prompts the application
* to close the old handle and re-enumerate the device.
*
/* Allocate persistent DMA memory for the given device, suitable for
* zerocopy. May return NULL on failure. Optional to implement.
*/
- unsigned char *(*dev_mem_alloc)(struct libusb_device_handle *handle,
- size_t len);
+ void *(*dev_mem_alloc)(struct libusb_device_handle *handle, size_t len);
/* Free memory allocated by dev_mem_alloc. */
- int (*dev_mem_free)(struct libusb_device_handle *handle,
- unsigned char *buffer, size_t len);
+ int (*dev_mem_free)(struct libusb_device_handle *handle, void *buffer,
+ size_t len);
/* Determine if a kernel driver is active on an interface. Optional.
*
* - another LIBUSB_ERROR code on other failure
*/
int (*kernel_driver_active)(struct libusb_device_handle *dev_handle,
- int interface_number);
+ uint8_t interface_number);
/* Detach a kernel driver from an interface. Optional.
*
* - another LIBUSB_ERROR code on other failure
*/
int (*detach_kernel_driver)(struct libusb_device_handle *dev_handle,
- int interface_number);
+ uint8_t interface_number);
/* Attach a kernel driver to an interface. Optional.
*
* - another LIBUSB_ERROR code on other failure
*/
int (*attach_kernel_driver)(struct libusb_device_handle *dev_handle,
- int interface_number);
+ uint8_t interface_number);
/* Destroy a device. Optional.
*
*/
void (*clear_transfer_priv)(struct usbi_transfer *itransfer);
- /* Handle any pending events on file descriptors. Optional.
+ /* Handle any pending events on event sources. Optional.
*
- * Provide this function when file descriptors directly indicate device
- * or transfer activity. If your backend does not have such file descriptors,
+ * Provide this function when event sources directly indicate device
+ * or transfer activity. If your backend does not have such event sources,
* implement the handle_transfer_completion function below.
*
* This involves monitoring any active transfers and processing their
* completion or cancellation.
*
- * The function is passed an array of pollfd structures (size nfds)
- * as a result of the poll() system call. The num_ready parameter
- * indicates the number of file descriptors that have reported events
- * (i.e. the poll() return value). This should be enough information
- * for you to determine which actions need to be taken on the currently
- * active transfers.
+ * The function is passed a pointer that represents platform-specific
+ * data for monitoring event sources (size count). This data is to be
+ * (re)allocated as necessary when event sources are modified.
+ * The num_ready parameter indicates the number of event sources that
+ * have reported events. This should be enough information for you to
+ * determine which actions need to be taken on the currently active
+ * transfers.
*
* For any cancelled transfers, call usbi_handle_transfer_cancellation().
* For completed transfers, call usbi_handle_transfer_completion().
* Return 0 on success, or a LIBUSB_ERROR code on failure.
*/
int (*handle_events)(struct libusb_context *ctx,
- struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready);
+ void *event_data, unsigned int count, unsigned int num_ready);
/* Handle transfer completion. Optional.
*
- * Provide this function when there are no file descriptors available
- * that directly indicate device or transfer activity. If your backend does
- * have such file descriptors, implement the handle_events function above.
+ * Provide this function when there are no event sources available that
+ * directly indicate device or transfer activity. If your backend does
+ * have such event sources, implement the handle_events function above.
*
* Your backend must tell the library when a transfer has completed by
* calling usbi_signal_transfer_completion(). You should store any private
*/
int (*handle_transfer_completion)(struct usbi_transfer *itransfer);
- /* Get time from specified clock. At least two clocks must be implemented
- by the backend: USBI_CLOCK_REALTIME, and USBI_CLOCK_MONOTONIC.
-
- Description of clocks:
- USBI_CLOCK_REALTIME : clock returns time since system epoch.
- USBI_CLOCK_MONOTONIC: clock returns time since unspecified start
- time (usually boot).
- */
- int (*clock_gettime)(int clkid, struct timespec *tp);
-
-#ifdef USBI_TIMERFD_AVAILABLE
- /* clock ID of the clock that should be used for timerfd */
- clockid_t (*get_timerfd_clockid)(void);
-#endif
-
/* Number of bytes to reserve for per-context private backend data.
- * This private data area is accessible through the "os_priv" field of
- * struct libusb_context. */
+ * This private data area is accessible by calling
+ * usbi_get_context_priv() on the libusb_context instance.
+ */
size_t context_priv_size;
/* Number of bytes to reserve for per-device private backend data.
- * This private data area is accessible through the "os_priv" field of
- * struct libusb_device. */
+ * This private data area is accessible by calling
+ * usbi_get_device_priv() on the libusb_device instance.
+ */
size_t device_priv_size;
/* Number of bytes to reserve for per-handle private backend data.
- * This private data area is accessible through the "os_priv" field of
- * struct libusb_device. */
+ * This private data area is accessible by calling
+ * usbi_get_device_handle_priv() on the libusb_device_handle instance.
+ */
size_t device_handle_priv_size;
/* Number of bytes to reserve for per-transfer private backend data.
* This private data area is accessible by calling
- * usbi_transfer_get_os_priv() on the appropriate usbi_transfer instance.
+ * usbi_get_transfer_priv() on the usbi_transfer instance.
*/
size_t transfer_priv_size;
};
extern const struct usbi_os_backend usbi_backend;
-extern struct list_head active_contexts_list;
-extern usbi_mutex_static_t active_contexts_lock;
+#define for_each_context(c) \
+ for_each_helper(c, &active_contexts_list, struct libusb_context)
+
+#define for_each_device(ctx, d) \
+ for_each_helper(d, &(ctx)->usb_devs, struct libusb_device)
+
+#define for_each_device_safe(ctx, d, n) \
+ for_each_safe_helper(d, n, &(ctx)->usb_devs, struct libusb_device)
+
+#define for_each_open_device(ctx, h) \
+ for_each_helper(h, &(ctx)->open_devs, struct libusb_device_handle)
+
+#define __for_each_transfer(list, t) \
+ for_each_helper(t, (list), struct usbi_transfer)
+
+#define for_each_transfer(ctx, t) \
+ __for_each_transfer(&(ctx)->flying_transfers, t)
+
+#define __for_each_transfer_safe(list, t, n) \
+ for_each_safe_helper(t, n, (list), struct usbi_transfer)
+
+#define for_each_transfer_safe(ctx, t, n) \
+ __for_each_transfer_safe(&(ctx)->flying_transfers, t, n)
+
+#define __for_each_completed_transfer_safe(list, t, n) \
+ list_for_each_entry_safe(t, n, (list), completed_list, struct usbi_transfer)
+
+#define for_each_event_source(ctx, e) \
+ for_each_helper(e, &(ctx)->event_sources, struct usbi_event_source)
+
+#define for_each_removed_event_source(ctx, e) \
+ for_each_helper(e, &(ctx)->removed_event_sources, struct usbi_event_source)
+
+#define for_each_removed_event_source_safe(ctx, e, n) \
+ for_each_safe_helper(e, n, &(ctx)->removed_event_sources, struct usbi_event_source)
#ifdef __cplusplus
}
/* -*- Mode: C; indent-tabs-mode:nil -*- */
/*
* darwin backend for libusb 1.0
- * Copyright © 2008-2017 Nathan Hjelm <hjelmn@users.sourceforge.net>
+ * Copyright © 2008-2020 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2019-2020 Google LLC. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "config.h"
+#include <config.h>
+#include <assert.h>
#include <time.h>
#include <ctype.h>
-#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <objc/objc-auto.h>
#endif
-#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
-/* Apple deprecated the darwin atomics in 10.12 in favor of C11 atomics */
-#include <stdatomic.h>
-#define libusb_darwin_atomic_fetch_add(x, y) atomic_fetch_add(x, y)
-
-_Atomic int32_t initCount = ATOMIC_VAR_INIT(0);
-#else
-/* use darwin atomics if the target is older than 10.12 */
-#include <libkern/OSAtomic.h>
-
-/* OSAtomicAdd32Barrier returns the new value */
-#define libusb_darwin_atomic_fetch_add(x, y) (OSAtomicAdd32Barrier(y, x) - y)
-
-static volatile int32_t initCount = 0;
-
-#endif
-
-/* On 10.12 and later, use newly available clock_*() functions */
-#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
-#define OSX_USE_CLOCK_GETTIME 1
-#else
-#define OSX_USE_CLOCK_GETTIME 0
-#endif
-
#include "darwin_usb.h"
+static pthread_mutex_t libusb_darwin_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int init_count = 0;
+
/* async event thread */
static pthread_mutex_t libusb_darwin_at_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER;
-static pthread_once_t darwin_init_once = PTHREAD_ONCE_INIT;
-
-#if !OSX_USE_CLOCK_GETTIME
+#if !defined(HAVE_CLOCK_GETTIME)
static clock_serv_t clock_realtime;
static clock_serv_t clock_monotonic;
#endif
+#define LIBUSB_DARWIN_STARTUP_FAILURE ((CFRunLoopRef) -1)
+
static CFRunLoopRef libusb_darwin_acfl = NULL; /* event cf loop */
static CFRunLoopSourceRef libusb_darwin_acfls = NULL; /* shutdown signal for event cf loop */
static usbi_mutex_t darwin_cached_devices_lock = PTHREAD_MUTEX_INITIALIZER;
-static struct list_head darwin_cached_devices = {&darwin_cached_devices, &darwin_cached_devices};
-static const char *darwin_device_class = kIOUSBDeviceClassName;
+static struct list_head darwin_cached_devices;
+static const char *darwin_device_class = "IOUSBDevice";
-#define DARWIN_CACHED_DEVICE(a) ((struct darwin_cached_device *) (((struct darwin_device_priv *)((a)->os_priv))->dev))
+#define DARWIN_CACHED_DEVICE(a) (((struct darwin_device_priv *)usbi_get_device_priv((a)))->dev)
/* async event thread */
static pthread_t libusb_darwin_at;
-static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian);
-static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface);
-static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface);
+static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len);
+static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface);
+static int darwin_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface);
static int darwin_reset_device(struct libusb_device_handle *dev_handle);
static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0);
-static int darwin_scan_devices(struct libusb_context *ctx);
-static int process_new_device (struct libusb_context *ctx, io_service_t service);
+static enum libusb_error darwin_scan_devices(struct libusb_context *ctx);
+static enum libusb_error process_new_device (struct libusb_context *ctx, struct darwin_cached_device *cached_device,
+ UInt64 old_session_id);
+
+static enum libusb_error darwin_get_cached_device(io_service_t service, struct darwin_cached_device **cached_out,
+ UInt64 *old_session_id);
#if defined(ENABLE_LOGGING)
-static const char *darwin_error_str (int result) {
+static const char *darwin_error_str (IOReturn result) {
static char string_buffer[50];
switch (result) {
case kIOReturnSuccess:
return "out of resources";
case kIOUSBHighSpeedSplitError:
return "high speed split error";
+ case kIOUSBUnknownPipeErr:
+ return "pipe ref not recognized";
default:
snprintf(string_buffer, sizeof(string_buffer), "unknown error (0x%x)", result);
return string_buffer;
}
#endif
-static int darwin_to_libusb (int result) {
+static enum libusb_error darwin_to_libusb (IOReturn result) {
switch (result) {
case kIOReturnUnderrun:
case kIOReturnSuccess:
case kIOReturnAborted:
case kIOReturnError:
case kIOUSBNoAsyncPortErr:
+ case kIOUSBUnknownPipeErr:
default:
return LIBUSB_ERROR_OTHER;
}
if (0 == cached_dev->refcount) {
list_del(&cached_dev->list);
- (*(cached_dev->device))->Release(cached_dev->device);
+ if (cached_dev->device) {
+ (*(cached_dev->device))->Release(cached_dev->device);
+ cached_dev->device = NULL;
+ }
free (cached_dev);
}
}
}
static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, uint8_t *pipep, uint8_t *ifcp, struct darwin_interface **interface_out) {
- struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+ struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
/* current interface */
struct darwin_interface *cInterface;
- int8_t i, iface;
+ uint8_t i, iface;
usbi_dbg ("converting ep address 0x%02x to pipeRef and interface", ep);
for (iface = 0 ; iface < USB_MAXINTERFACES ; iface++) {
cInterface = &priv->interfaces[iface];
- if (dev_handle->claimed_interfaces & (1 << iface)) {
+ if (dev_handle->claimed_interfaces & (1U << iface)) {
for (i = 0 ; i < cInterface->num_endpoints ; i++) {
if (cInterface->endpoint_addrs[i] == ep) {
*pipep = i + 1;
*interface_out = cInterface;
usbi_dbg ("pipe %d on interface %d matches", *pipep, iface);
- return 0;
+ return LIBUSB_SUCCESS;
}
}
}
return LIBUSB_ERROR_NOT_FOUND;
}
-static int usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 location) {
+static IOReturn usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 location) {
CFMutableDictionaryRef matchingDict = IOServiceMatching(darwin_device_class);
if (!matchingDict)
}
/* Returns 1 on success, 0 on failure. */
-static int get_ioregistry_value_number (io_service_t service, CFStringRef property, CFNumberType type, void *p) {
+static bool get_ioregistry_value_number (io_service_t service, CFStringRef property, CFNumberType type, void *p) {
CFTypeRef cfNumber = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0);
- int ret = 0;
+ Boolean success = 0;
if (cfNumber) {
if (CFGetTypeID(cfNumber) == CFNumberGetTypeID()) {
- ret = CFNumberGetValue(cfNumber, type, p);
+ success = CFNumberGetValue(cfNumber, type, p);
}
CFRelease (cfNumber);
}
- return ret;
+ return (success != 0);
}
-static int get_ioregistry_value_data (io_service_t service, CFStringRef property, ssize_t size, void *p) {
+/* Returns 1 on success, 0 on failure. */
+static bool get_ioregistry_value_data (io_service_t service, CFStringRef property, ssize_t size, void *p) {
CFTypeRef cfData = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0);
- int ret = 0;
+ bool success = false;
if (cfData) {
if (CFGetTypeID (cfData) == CFDataGetTypeID ()) {
}
CFDataGetBytes (cfData, CFRangeMake(0, size), p);
- ret = 1;
+ success = true;
}
CFRelease (cfData);
}
- return ret;
+ return success;
}
static usb_device_t **darwin_device_from_service (io_service_t service)
{
io_cf_plugin_ref_t *plugInInterface = NULL;
usb_device_t **device;
- kern_return_t result;
+ IOReturn kresult;
SInt32 score;
+ const int max_retries = 5;
+
+ /* The IOCreatePlugInInterfaceForService function might consistently return
+ an "out of resources" error with certain USB devices the first time we run
+ it. The reason is still unclear, but retrying fixes the problem */
+ for (int count = 0; count < max_retries; count++) {
+ kresult = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID, &plugInInterface,
+ &score);
+ if (kIOReturnSuccess == kresult && plugInInterface) {
+ break;
+ }
- result = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID,
- kIOCFPlugInInterfaceID, &plugInInterface,
- &score);
+ usbi_dbg ("set up plugin for service retry: %s", darwin_error_str (kresult));
- if (kIOReturnSuccess != result || !plugInInterface) {
- usbi_dbg ("could not set up plugin for service: %s", darwin_error_str (result));
+ /* sleep for a little while before trying again */
+ nanosleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 1000}, NULL);
+ }
+
+ if (kIOReturnSuccess != kresult || !plugInInterface) {
+ usbi_dbg ("could not set up plugin for service: %s", darwin_error_str (kresult));
return NULL;
}
static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
UNUSED(ptr);
+ struct darwin_cached_device *cached_device;
+ UInt64 old_session_id;
struct libusb_context *ctx;
io_service_t service;
+ int ret;
usbi_mutex_lock(&active_contexts_lock);
while ((service = IOIteratorNext(add_devices))) {
+ ret = darwin_get_cached_device (service, &cached_device, &old_session_id);
+ if (ret < 0 || !cached_device->can_enumerate) {
+ continue;
+ }
+
/* add this device to each active context's device list */
- list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
- process_new_device (ctx, service);
+ for_each_context(ctx) {
+ process_new_device (ctx, cached_device, old_session_id);
+ }
+
+ if (cached_device->in_reenumerate) {
+ usbi_dbg ("cached device in reset state. reset complete...");
+ cached_device->in_reenumerate = false;
}
IOObjectRelease(service);
usbi_mutex_lock(&active_contexts_lock);
while ((device = IOIteratorNext (rem_devices)) != 0) {
+ bool is_reenumerating = false;
+
/* get the location from the i/o registry */
ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session);
IOObjectRelease (device);
usbi_mutex_lock(&darwin_cached_devices_lock);
list_for_each_entry(old_device, &darwin_cached_devices, list, struct darwin_cached_device) {
if (old_device->session == session) {
- darwin_deref_cached_device (old_device);
+ if (old_device->in_reenumerate) {
+ /* device is re-enumerating. do not dereference the device at this time. libusb_reset_device()
+ * will deref if needed. */
+ usbi_dbg ("detected device detached due to re-enumeration");
+
+ /* the device object is no longer usable so go ahead and release it */
+ if (old_device->device) {
+ (*(old_device->device))->Release(old_device->device);
+ old_device->device = NULL;
+ }
+
+ is_reenumerating = true;
+ } else {
+ darwin_deref_cached_device (old_device);
+ }
+
break;
}
}
+
usbi_mutex_unlock(&darwin_cached_devices_lock);
+ if (is_reenumerating) {
+ continue;
+ }
- list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+ for_each_context(ctx) {
usbi_dbg ("notifying context %p of device disconnect", ctx);
dev = usbi_get_device_by_session_id(ctx, (unsigned long) session);
static void darwin_hotplug_poll (void)
{
- /* not sure if 5 seconds will be too long/short but it should work ok */
- mach_timespec_t timeout = {.tv_sec = 5, .tv_nsec = 0};
+ /* not sure if 1 ms will be too long/short but it should work ok */
+ mach_timespec_t timeout = {.tv_sec = 0, .tv_nsec = 1000000ul};
- /* since a kernel thread may nodify the IOInterators used for
- * hotplug notidication we can't just clear the iterators.
+ /* since a kernel thread may notify the IOIterators used for
+ * hotplug notification we can't just clear the iterators.
* instead just wait until all IOService providers are quiet */
(void) IOKitWaitQuiet (kIOMasterPortDefault, &timeout);
}
IOObjectRelease (device);
}
+static void darwin_fail_startup(void) {
+ pthread_mutex_lock (&libusb_darwin_at_mutex);
+ libusb_darwin_acfl = LIBUSB_DARWIN_STARTUP_FAILURE;
+ pthread_cond_signal (&libusb_darwin_at_cond);
+ pthread_mutex_unlock (&libusb_darwin_at_mutex);
+ pthread_exit (NULL);
+}
+
static void *darwin_event_thread_main (void *arg0) {
IOReturn kresult;
struct libusb_context *ctx = (struct libusb_context *)arg0;
CFRunLoopRef runloop;
+ CFRunLoopSourceRef libusb_shutdown_cfsource;
+ CFRunLoopSourceContext libusb_shutdown_cfsourcectx;
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
/* Set this thread's name, so it can be seen in the debugger
#endif
/* hotplug (device arrival/removal) sources */
- CFRunLoopSourceContext libusb_shutdown_cfsourcectx;
CFRunLoopSourceRef libusb_notification_cfsource;
io_notification_port_t libusb_notification_port;
io_iterator_t libusb_rem_device_iterator;
memset(&libusb_shutdown_cfsourcectx, 0, sizeof(libusb_shutdown_cfsourcectx));
libusb_shutdown_cfsourcectx.info = runloop;
libusb_shutdown_cfsourcectx.perform = (void (*)(void *))CFRunLoopStop;
- libusb_darwin_acfls = CFRunLoopSourceCreate(NULL, 0, &libusb_shutdown_cfsourcectx);
- CFRunLoopAddSource(runloop, libusb_darwin_acfls, kCFRunLoopDefaultMode);
+ libusb_shutdown_cfsource = CFRunLoopSourceCreate(NULL, 0, &libusb_shutdown_cfsourcectx);
+ CFRunLoopAddSource(runloop, libusb_shutdown_cfsource, kCFRunLoopDefaultMode);
/* add the notification port to the run loop */
libusb_notification_port = IONotificationPortCreate (kIOMasterPortDefault);
if (kresult != kIOReturnSuccess) {
usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
-
- pthread_exit (NULL);
+ CFRelease (libusb_shutdown_cfsource);
+ CFRelease (runloop);
+ darwin_fail_startup ();
}
/* create notifications for attached devices */
if (kresult != kIOReturnSuccess) {
usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
-
- pthread_exit (NULL);
+ CFRelease (libusb_shutdown_cfsource);
+ CFRelease (runloop);
+ darwin_fail_startup ();
}
/* arm notifiers */
/* signal the main thread that the hotplug runloop has been created. */
pthread_mutex_lock (&libusb_darwin_at_mutex);
libusb_darwin_acfl = runloop;
+ libusb_darwin_acfls = libusb_shutdown_cfsource;
pthread_cond_signal (&libusb_darwin_at_cond);
pthread_mutex_unlock (&libusb_darwin_at_mutex);
usbi_dbg ("darwin event thread exiting");
+ /* signal the main thread that the hotplug runloop has finished. */
+ pthread_mutex_lock (&libusb_darwin_at_mutex);
+ libusb_darwin_acfls = NULL;
+ libusb_darwin_acfl = NULL;
+ pthread_cond_signal (&libusb_darwin_at_cond);
+ pthread_mutex_unlock (&libusb_darwin_at_mutex);
+
/* remove the notification cfsource */
CFRunLoopRemoveSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode);
/* remove the shutdown cfsource */
- CFRunLoopRemoveSource(runloop, libusb_darwin_acfls, kCFRunLoopDefaultMode);
+ CFRunLoopRemoveSource(runloop, libusb_shutdown_cfsource, kCFRunLoopDefaultMode);
/* delete notification port */
IONotificationPortDestroy (libusb_notification_port);
IOObjectRelease (libusb_rem_device_iterator);
IOObjectRelease (libusb_add_device_iterator);
- CFRelease (libusb_darwin_acfls);
+ CFRelease (libusb_shutdown_cfsource);
CFRelease (runloop);
- libusb_darwin_acfls = NULL;
- libusb_darwin_acfl = NULL;
-
pthread_exit (NULL);
}
/* cleanup function to destroy cached devices */
-static void __attribute__((destructor)) _darwin_finalize(void) {
+static void darwin_cleanup_devices(void) {
struct darwin_cached_device *dev, *next;
- usbi_mutex_lock(&darwin_cached_devices_lock);
list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) {
darwin_deref_cached_device(dev);
}
- usbi_mutex_unlock(&darwin_cached_devices_lock);
-}
-
-static void darwin_check_version (void) {
- /* adjust for changes in the USB stack in xnu 15 */
- int sysctl_args[] = {CTL_KERN, KERN_OSRELEASE};
- long version;
- char version_string[256] = {'\0',};
- size_t length = 256;
- sysctl(sysctl_args, 2, version_string, &length, NULL, 0);
-
- errno = 0;
- version = strtol (version_string, NULL, 10);
- if (0 == errno && version >= 15) {
- darwin_device_class = "IOUSBHostDevice";
- }
+ darwin_cached_devices.prev = darwin_cached_devices.next = NULL;
}
static int darwin_init(struct libusb_context *ctx) {
+ bool first_init;
int rc;
- rc = pthread_once (&darwin_init_once, darwin_check_version);
- if (rc) {
- return LIBUSB_ERROR_OTHER;
- }
-
- rc = darwin_scan_devices (ctx);
- if (LIBUSB_SUCCESS != rc) {
- return rc;
- }
+ pthread_mutex_lock (&libusb_darwin_init_mutex);
- if (libusb_darwin_atomic_fetch_add (&initCount, 1) == 0) {
-#if !OSX_USE_CLOCK_GETTIME
- /* create the clocks that will be used if clock_gettime() is not available */
- host_name_port_t host_self;
+ first_init = (1 == ++init_count);
- host_self = mach_host_self();
- host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime);
- host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic);
- mach_port_deallocate(mach_task_self(), host_self);
+ do {
+ if (first_init) {
+ assert (NULL == darwin_cached_devices.next);
+ list_init (&darwin_cached_devices);
+
+#if !defined(HAVE_CLOCK_GETTIME)
+ /* create the clocks that will be used if clock_gettime() is not available */
+ host_name_port_t host_self;
+
+ host_self = mach_host_self();
+ host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime);
+ host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic);
+ mach_port_deallocate(mach_task_self(), host_self);
#endif
+ }
- pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx);
+ rc = darwin_scan_devices (ctx);
+ if (LIBUSB_SUCCESS != rc)
+ break;
- pthread_mutex_lock (&libusb_darwin_at_mutex);
- while (!libusb_darwin_acfl)
- pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex);
- pthread_mutex_unlock (&libusb_darwin_at_mutex);
+ if (first_init) {
+ rc = pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx);
+ if (0 != rc) {
+ usbi_err (ctx, "could not create event thread, error %d", rc);
+ rc = LIBUSB_ERROR_OTHER;
+ break;
+ }
+
+ pthread_mutex_lock (&libusb_darwin_at_mutex);
+ while (!libusb_darwin_acfl)
+ pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex);
+ if (libusb_darwin_acfl == LIBUSB_DARWIN_STARTUP_FAILURE) {
+ libusb_darwin_acfl = NULL;
+ rc = LIBUSB_ERROR_OTHER;
+ }
+ pthread_mutex_unlock (&libusb_darwin_at_mutex);
+
+ if (0 != rc)
+ pthread_join (libusb_darwin_at, NULL);
+ }
+ } while (0);
+
+ if (LIBUSB_SUCCESS != rc) {
+ if (first_init) {
+ darwin_cleanup_devices ();
+#if !defined(HAVE_CLOCK_GETTIME)
+ mach_port_deallocate(mach_task_self(), clock_realtime);
+ mach_port_deallocate(mach_task_self(), clock_monotonic);
+#endif
+ }
+ --init_count;
}
+ pthread_mutex_unlock (&libusb_darwin_init_mutex);
+
return rc;
}
static void darwin_exit (struct libusb_context *ctx) {
UNUSED(ctx);
- if (libusb_darwin_atomic_fetch_add (&initCount, -1) == 1) {
-#if !OSX_USE_CLOCK_GETTIME
- mach_port_deallocate(mach_task_self(), clock_realtime);
- mach_port_deallocate(mach_task_self(), clock_monotonic);
-#endif
+ pthread_mutex_lock (&libusb_darwin_init_mutex);
+
+ if (0 == --init_count) {
/* stop the event runloop and wait for the thread to terminate. */
- CFRunLoopSourceSignal(libusb_darwin_acfls);
+ pthread_mutex_lock (&libusb_darwin_at_mutex);
+ CFRunLoopSourceSignal (libusb_darwin_acfls);
CFRunLoopWakeUp (libusb_darwin_acfl);
+ while (libusb_darwin_acfl)
+ pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex);
+ pthread_mutex_unlock (&libusb_darwin_at_mutex);
pthread_join (libusb_darwin_at, NULL);
- }
-}
-static int darwin_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) {
- struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
-
- /* return cached copy */
- memmove (buffer, &(priv->dev_descriptor), DEVICE_DESC_LENGTH);
+ darwin_cleanup_devices ();
- *host_endian = 0;
+#if !defined(HAVE_CLOCK_GETTIME)
+ mach_port_deallocate(mach_task_self(), clock_realtime);
+ mach_port_deallocate(mach_task_self(), clock_monotonic);
+#endif
+ }
- return 0;
+ pthread_mutex_unlock (&libusb_darwin_init_mutex);
}
-static int get_configuration_index (struct libusb_device *dev, int config_value) {
+static int get_configuration_index (struct libusb_device *dev, UInt8 config_value) {
struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
UInt8 i, numConfig;
IOUSBConfigurationDescriptorPtr desc;
return LIBUSB_ERROR_NOT_FOUND;
}
-static int darwin_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) {
+static int darwin_get_active_config_descriptor(struct libusb_device *dev, void *buffer, size_t len) {
struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
int config_index;
if (config_index < 0)
return config_index;
- return darwin_get_config_descriptor (dev, config_index, buffer, len, host_endian);
+ assert(config_index >= 0 && config_index <= UINT8_MAX);
+ return darwin_get_config_descriptor (dev, (UInt8)config_index, buffer, len);
}
-static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) {
+static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len) {
struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
IOUSBConfigurationDescriptorPtr desc;
IOReturn kresult;
len = libusb_le16_to_cpu(desc->wTotalLength);
memmove (buffer, desc, len);
-
- /* GetConfigurationDescriptorPtr returns the descriptor in USB bus order */
- *host_endian = 0;
}
ret = darwin_to_libusb (kresult);
}
/* check whether the os has configured the device */
-static int darwin_check_configuration (struct libusb_context *ctx, struct darwin_cached_device *dev) {
+static enum libusb_error darwin_check_configuration (struct libusb_context *ctx, struct darwin_cached_device *dev) {
usb_device_t **darwin_device = dev->device;
IOUSBConfigurationDescriptorPtr configDesc;
IOUSBFindInterfaceRequest request;
- kern_return_t kresult;
+ IOReturn kresult;
io_iterator_t interface_iterator;
io_service_t firstInterface;
/* checking the configuration of a root hub simulation takes ~1 s in 10.11. the device is
not usable anyway */
- if (0x05ac == dev->dev_descriptor.idVendor && 0x8005 == dev->dev_descriptor.idProduct) {
+ if (0x05ac == libusb_le16_to_cpu (dev->dev_descriptor.idVendor) &&
+ 0x8005 == libusb_le16_to_cpu (dev->dev_descriptor.idProduct)) {
usbi_dbg ("ignoring configuration on root hub simulation");
dev->active_config = 0;
- return 0;
+ return LIBUSB_SUCCESS;
}
/* find the first configuration */
request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator);
- if (kresult)
+ if (kresult != kIOReturnSuccess)
return darwin_to_libusb (kresult);
/* iterate once */
} else
/* not configured */
dev->active_config = 0;
-
+
usbi_dbg ("active config: %u, first config: %u", dev->active_config, dev->first_config);
- return 0;
+ return LIBUSB_SUCCESS;
}
-static int darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) {
+static IOReturn darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) {
IOUSBDevRequestTO req;
+ assert(buffer_size <= UINT16_MAX);
+
memset (buffer, 0, buffer_size);
/* Set up request for descriptor/ */
req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
req.bRequest = kUSBRqGetDescriptor;
- req.wValue = desc << 8;
+ req.wValue = (UInt16)(desc << 8);
req.wIndex = desc_index;
- req.wLength = buffer_size;
+ req.wLength = (UInt16)buffer_size;
req.pData = buffer;
req.noDataTimeout = 20;
req.completionTimeout = 100;
return (*device)->DeviceRequestTO (device, &req);
}
-static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct darwin_cached_device *dev) {
+static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_device *dev) {
usb_device_t **device = dev->device;
- int retries = 1, delay = 30000;
+ int retries = 1;
+ long delay = 30000; // microseconds
int unsuspended = 0, try_unsuspend = 1, try_reconfigure = 1;
int is_open = 0;
- int ret = 0, ret2;
+ IOReturn ret = 0, ret2;
UInt8 bDeviceClass;
UInt16 idProduct, idVendor;
(void)(*device)->GetUSBDeviceInformation (device, &info);
/* note that the device was suspended */
- if (info & (1 << kUSBInformationDeviceIsSuspendedBit) || 0 == info)
+ if (info & (1U << kUSBInformationDeviceIsSuspendedBit) || 0 == info)
try_unsuspend = 1;
#endif
}
if (kIOReturnSuccess != ret) {
- usbi_dbg("kernel responded with code: 0x%08x. sleeping for %d ms before trying again", ret, delay/1000);
+ usbi_dbg("kernel responded with code: 0x%08x. sleeping for %ld ms before trying again", ret, delay/1000);
/* sleep for a little while before trying again */
- nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000UL}, NULL);
+ nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000}, NULL);
}
} while (kIOReturnSuccess != ret && retries--);
usbi_dbg ("could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
idVendor, idProduct, darwin_error_str (ret), ret);
else
- usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
+ usbi_warn (NULL, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
idVendor, idProduct, darwin_error_str (ret), ret);
return darwin_to_libusb (ret);
}
/* catch buggy hubs (which appear to be virtual). Apple's own USB prober has problems with these devices. */
if (libusb_le16_to_cpu (dev->dev_descriptor.idProduct) != idProduct) {
/* not a valid device */
- usbi_warn (ctx, "idProduct from iokit (%04x) does not match idProduct in descriptor (%04x). skipping device",
+ usbi_warn (NULL, "idProduct from iokit (%04x) does not match idProduct in descriptor (%04x). skipping device",
idProduct, libusb_le16_to_cpu (dev->dev_descriptor.idProduct));
return LIBUSB_ERROR_NO_DEVICE;
}
usbi_dbg ("cached device descriptor:");
usbi_dbg (" bDescriptorType: 0x%02x", dev->dev_descriptor.bDescriptorType);
- usbi_dbg (" bcdUSB: 0x%04x", dev->dev_descriptor.bcdUSB);
+ usbi_dbg (" bcdUSB: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdUSB));
usbi_dbg (" bDeviceClass: 0x%02x", dev->dev_descriptor.bDeviceClass);
usbi_dbg (" bDeviceSubClass: 0x%02x", dev->dev_descriptor.bDeviceSubClass);
usbi_dbg (" bDeviceProtocol: 0x%02x", dev->dev_descriptor.bDeviceProtocol);
usbi_dbg (" bMaxPacketSize0: 0x%02x", dev->dev_descriptor.bMaxPacketSize0);
- usbi_dbg (" idVendor: 0x%04x", dev->dev_descriptor.idVendor);
- usbi_dbg (" idProduct: 0x%04x", dev->dev_descriptor.idProduct);
- usbi_dbg (" bcdDevice: 0x%04x", dev->dev_descriptor.bcdDevice);
+ usbi_dbg (" idVendor: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idVendor));
+ usbi_dbg (" idProduct: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idProduct));
+ usbi_dbg (" bcdDevice: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdDevice));
usbi_dbg (" iManufacturer: 0x%02x", dev->dev_descriptor.iManufacturer);
usbi_dbg (" iProduct: 0x%02x", dev->dev_descriptor.iProduct);
usbi_dbg (" iSerialNumber: 0x%02x", dev->dev_descriptor.iSerialNumber);
return LIBUSB_SUCCESS;
}
-static int get_device_port (io_service_t service, UInt8 *port) {
- kern_return_t result;
+/* Returns 1 on success, 0 on failure. */
+static bool get_device_port (io_service_t service, UInt8 *port) {
+ IOReturn kresult;
io_service_t parent;
- int ret = 0;
+ bool ret = false;
if (get_ioregistry_value_number (service, CFSTR("PortNum"), kCFNumberSInt8Type, port)) {
- return 1;
+ return true;
}
- result = IORegistryEntryGetParentEntry (service, kIOServicePlane, &parent);
- if (kIOReturnSuccess == result) {
+ kresult = IORegistryEntryGetParentEntry (service, kIOServicePlane, &parent);
+ if (kIOReturnSuccess == kresult) {
ret = get_ioregistry_value_data (parent, CFSTR("port"), 1, port);
IOObjectRelease (parent);
}
return ret;
}
-static int get_device_parent_sessionID(io_service_t service, UInt64 *parent_sessionID) {
- kern_return_t result;
+/* Returns 1 on success, 0 on failure. */
+static bool get_device_parent_sessionID(io_service_t service, UInt64 *parent_sessionID) {
+ IOReturn kresult;
io_service_t parent;
/* Walk up the tree in the IOService plane until we find a parent that has a sessionID */
parent = service;
- while((result = IORegistryEntryGetParentEntry (parent, kIOServicePlane, &parent)) == kIOReturnSuccess) {
+ while((kresult = IORegistryEntryGetParentEntry (parent, kIOUSBPlane, &parent)) == kIOReturnSuccess) {
if (get_ioregistry_value_number (parent, CFSTR("sessionID"), kCFNumberSInt64Type, parent_sessionID)) {
/* Success */
- return 1;
+ return true;
}
}
/* We ran out of parents */
- return 0;
+ return false;
}
-static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t service,
- struct darwin_cached_device **cached_out) {
+static enum libusb_error darwin_get_cached_device(io_service_t service, struct darwin_cached_device **cached_out,
+ UInt64 *old_session_id) {
struct darwin_cached_device *new_device;
UInt64 sessionID = 0, parent_sessionID = 0;
- int ret = LIBUSB_SUCCESS;
+ UInt32 locationID = 0;
+ enum libusb_error ret = LIBUSB_SUCCESS;
usb_device_t **device;
UInt8 port = 0;
+ /* assuming sessionID != 0 normally (never seen it be 0) */
+ *old_session_id = 0;
+ *cached_out = NULL;
+
/* get some info from the io registry */
(void) get_ioregistry_value_number (service, CFSTR("sessionID"), kCFNumberSInt64Type, &sessionID);
+ (void) get_ioregistry_value_number (service, CFSTR("locationID"), kCFNumberSInt32Type, &locationID);
if (!get_device_port (service, &port)) {
usbi_dbg("could not get connected port number");
}
usbi_mutex_lock(&darwin_cached_devices_lock);
do {
- *cached_out = NULL;
-
list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) {
- usbi_dbg("matching sessionID 0x%" PRIx64 " against cached device with sessionID 0x%" PRIx64, sessionID, new_device->session);
+ usbi_dbg("matching sessionID/locationID 0x%" PRIx64 "/0x%x against cached device with sessionID/locationID 0x%" PRIx64 "/0x%x",
+ sessionID, locationID, new_device->session, new_device->location);
+ if (new_device->location == locationID && new_device->in_reenumerate) {
+ usbi_dbg ("found cached device with matching location that is being re-enumerated");
+ *old_session_id = new_device->session;
+ break;
+ }
+
if (new_device->session == sessionID) {
usbi_dbg("using cached device for device");
*cached_out = new_device;
break;
}
- new_device = calloc (1, sizeof (*new_device));
- if (!new_device) {
- ret = LIBUSB_ERROR_NO_MEM;
- break;
- }
+ if (!(*old_session_id)) {
+ new_device = calloc (1, sizeof (*new_device));
+ if (!new_device) {
+ ret = LIBUSB_ERROR_NO_MEM;
+ break;
+ }
- /* add this device to the cached device list */
- list_add(&new_device->list, &darwin_cached_devices);
+ /* add this device to the cached device list */
+ list_add(&new_device->list, &darwin_cached_devices);
- (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&new_device->address);
+ (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&new_device->address);
- /* keep a reference to this device */
- darwin_ref_cached_device(new_device);
+ /* keep a reference to this device */
+ darwin_ref_cached_device(new_device);
+
+ (*device)->GetLocationID (device, &new_device->location);
+ new_device->port = port;
+ new_device->parent_session = parent_sessionID;
+ }
+
+ /* keep track of devices regardless of if we successfully enumerate them to
+ prevent them from being enumerated multiple times */
+ *cached_out = new_device;
- new_device->device = device;
new_device->session = sessionID;
- (*device)->GetLocationID (device, &new_device->location);
- new_device->port = port;
- new_device->parent_session = parent_sessionID;
+ new_device->device = device;
/* cache the device descriptor */
- ret = darwin_cache_device_descriptor(ctx, new_device);
+ ret = darwin_cache_device_descriptor(new_device);
if (ret)
break;
if (new_device->can_enumerate) {
snprintf(new_device->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", new_device->address,
- new_device->dev_descriptor.idVendor, new_device->dev_descriptor.idProduct,
+ libusb_le16_to_cpu (new_device->dev_descriptor.idVendor),
+ libusb_le16_to_cpu (new_device->dev_descriptor.idProduct),
new_device->dev_descriptor.bDeviceClass, new_device->dev_descriptor.bDeviceSubClass);
}
} while (0);
usbi_mutex_unlock(&darwin_cached_devices_lock);
- /* keep track of devices regardless of if we successfully enumerate them to
- prevent them from being enumerated multiple times */
-
- *cached_out = new_device;
-
return ret;
}
-static int process_new_device (struct libusb_context *ctx, io_service_t service) {
+static enum libusb_error process_new_device (struct libusb_context *ctx, struct darwin_cached_device *cached_device,
+ UInt64 old_session_id) {
struct darwin_device_priv *priv;
struct libusb_device *dev = NULL;
- struct darwin_cached_device *cached_device;
UInt8 devSpeed;
- int ret = 0;
+ enum libusb_error ret = LIBUSB_SUCCESS;
do {
- ret = darwin_get_cached_device (ctx, service, &cached_device);
-
- if (ret < 0 || !cached_device->can_enumerate) {
- return ret;
- }
-
/* check current active configuration (and cache the first configuration value--
which may be used by claim_interface) */
ret = darwin_check_configuration (ctx, cached_device);
if (ret)
break;
- usbi_dbg ("allocating new device in context %p for with session 0x%" PRIx64,
- ctx, cached_device->session);
+ if (0 != old_session_id) {
+ usbi_dbg ("re-using existing device from context %p for with session 0x%" PRIx64 " new session 0x%" PRIx64,
+ ctx, old_session_id, cached_device->session);
+ /* save the libusb device before the session id is updated */
+ dev = usbi_get_device_by_session_id (ctx, (unsigned long) old_session_id);
+ }
- dev = usbi_alloc_device(ctx, (unsigned long) cached_device->session);
if (!dev) {
- return LIBUSB_ERROR_NO_MEM;
- }
+ usbi_dbg ("allocating new device in context %p for with session 0x%" PRIx64,
+ ctx, cached_device->session);
- priv = (struct darwin_device_priv *)dev->os_priv;
+ dev = usbi_alloc_device(ctx, (unsigned long) cached_device->session);
+ if (!dev) {
+ return LIBUSB_ERROR_NO_MEM;
+ }
- priv->dev = cached_device;
- darwin_ref_cached_device (priv->dev);
+ priv = usbi_get_device_priv(dev);
+
+ priv->dev = cached_device;
+ darwin_ref_cached_device (priv->dev);
+ dev->port_number = cached_device->port;
+ dev->bus_number = cached_device->location >> 24;
+ assert(cached_device->address <= UINT8_MAX);
+ dev->device_address = (uint8_t)cached_device->address;
+ } else {
+ priv = usbi_get_device_priv(dev);
+ }
+
+ static_assert(sizeof(dev->device_descriptor) == sizeof(cached_device->dev_descriptor),
+ "mismatch between libusb and IOKit device descriptor sizes");
+ memcpy(&dev->device_descriptor, &cached_device->dev_descriptor, LIBUSB_DT_DEVICE_SIZE);
+ usbi_localize_device_descriptor(&dev->device_descriptor);
+ dev->session_data = cached_device->session;
if (cached_device->parent_session > 0) {
dev->parent_dev = usbi_get_device_by_session_id (ctx, (unsigned long) cached_device->parent_session);
} else {
dev->parent_dev = NULL;
}
- dev->port_number = cached_device->port;
- dev->bus_number = cached_device->location >> 24;
- dev->device_address = cached_device->address;
(*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed);
usbi_dbg ("found device with address %d port = %d parent = %p at %p", dev->device_address,
dev->port_number, (void *) dev->parent_dev, priv->dev->sys_path);
+
} while (0);
- if (0 == ret) {
+ if (!cached_device->in_reenumerate && 0 == ret) {
usbi_connect_device (dev);
} else {
libusb_unref_device (dev);
return ret;
}
-static int darwin_scan_devices(struct libusb_context *ctx) {
+static enum libusb_error darwin_scan_devices(struct libusb_context *ctx) {
+ struct darwin_cached_device *cached_device;
+ UInt64 old_session_id;
io_iterator_t deviceIterator;
io_service_t service;
- kern_return_t kresult;
+ IOReturn kresult;
+ int ret;
kresult = usb_setup_device_iterator (&deviceIterator, 0);
if (kresult != kIOReturnSuccess)
return darwin_to_libusb (kresult);
while ((service = IOIteratorNext (deviceIterator))) {
- (void) process_new_device (ctx, service);
+ ret = darwin_get_cached_device (service, &cached_device, &old_session_id);
+ if (ret < 0 || !cached_device->can_enumerate) {
+ continue;
+ }
+
+ (void) process_new_device (ctx, cached_device, old_session_id);
IOObjectRelease(service);
}
IOObjectRelease(deviceIterator);
- return 0;
+ return LIBUSB_SUCCESS;
}
static int darwin_open (struct libusb_device_handle *dev_handle) {
- struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+ struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
IOReturn kresult;
}
/* it is possible to perform some actions on a device that is not open so do not return an error */
- priv->is_open = 0;
+ priv->is_open = false;
} else {
- priv->is_open = 1;
+ priv->is_open = true;
}
/* create async event source */
(*(dpriv->device))->USBDeviceClose (dpriv->device);
}
- priv->is_open = 0;
+ priv->is_open = false;
return darwin_to_libusb (kresult);
}
}
static void darwin_close (struct libusb_device_handle *dev_handle) {
- struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+ struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
IOReturn kresult;
int i;
/* make sure all interfaces are released */
for (i = 0 ; i < USB_MAXINTERFACES ; i++)
- if (dev_handle->claimed_interfaces & (1 << i))
+ if (dev_handle->claimed_interfaces & (1U << i))
libusb_release_interface (dev_handle, i);
if (0 == dpriv->open_count) {
if (priv->is_open) {
/* close the device */
kresult = (*(dpriv->device))->USBDeviceClose(dpriv->device);
- if (kresult) {
+ if (kresult != kIOReturnSuccess) {
/* Log the fact that we had a problem closing the file, however failing a
* close isn't really an error, so return success anyway */
usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceClose: %s", darwin_error_str(kresult));
}
}
-static int darwin_get_configuration(struct libusb_device_handle *dev_handle, int *config) {
+static int darwin_get_configuration(struct libusb_device_handle *dev_handle, uint8_t *config) {
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
- *config = (int) dpriv->active_config;
+ *config = dpriv->active_config;
- return 0;
+ return LIBUSB_SUCCESS;
}
-static int darwin_set_configuration(struct libusb_device_handle *dev_handle, int config) {
+static enum libusb_error darwin_set_configuration(struct libusb_device_handle *dev_handle, int config) {
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
IOReturn kresult;
- int i;
+ uint8_t i;
+
+ if (config == -1)
+ config = 0;
/* Setting configuration will invalidate the interface, so we need
to reclaim it. First, dispose of existing interfaces, if any. */
for (i = 0 ; i < USB_MAXINTERFACES ; i++)
- if (dev_handle->claimed_interfaces & (1 << i))
+ if (dev_handle->claimed_interfaces & (1U << i))
darwin_release_interface (dev_handle, i);
- kresult = (*(dpriv->device))->SetConfiguration (dpriv->device, config);
+ kresult = (*(dpriv->device))->SetConfiguration (dpriv->device, (UInt8)config);
if (kresult != kIOReturnSuccess)
return darwin_to_libusb (kresult);
/* Reclaim any interfaces. */
for (i = 0 ; i < USB_MAXINTERFACES ; i++)
- if (dev_handle->claimed_interfaces & (1 << i))
+ if (dev_handle->claimed_interfaces & (1U << i))
darwin_claim_interface (dev_handle, i);
- dpriv->active_config = config;
+ dpriv->active_config = (UInt8)config;
- return 0;
+ return LIBUSB_SUCCESS;
}
-static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_service_t *usbInterfacep) {
+static IOReturn darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_service_t *usbInterfacep) {
IOUSBFindInterfaceRequest request;
- kern_return_t kresult;
+ IOReturn kresult;
io_iterator_t interface_iterator;
UInt8 bInterfaceNumber;
- int ret;
+ bool ret;
*usbInterfacep = IO_OBJECT_NULL;
request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator);
- if (kresult)
+ if (kresult != kIOReturnSuccess)
return kresult;
while ((*usbInterfacep = IOIteratorNext(interface_iterator))) {
/* done with the interface iterator */
IOObjectRelease(interface_iterator);
- return 0;
+ return kIOReturnSuccess;
}
-static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
- struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle, uint8_t iface) {
+ struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
/* current interface */
struct darwin_interface *cInterface = &priv->interfaces[iface];
- kern_return_t kresult;
+ IOReturn kresult;
UInt8 numep, direction, number;
UInt8 dont_care1, dont_care3;
/* retrieve the total number of endpoints on this interface */
kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep);
- if (kresult) {
+ if (kresult != kIOReturnSuccess) {
usbi_err (HANDLE_CTX (dev_handle), "can't get number of endpoints for interface: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
/* iterate through pipe references */
- for (int i = 1 ; i <= numep ; i++) {
+ for (UInt8 i = 1 ; i <= numep ; i++) {
kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1,
&dont_care2, &dont_care3);
UInt8 alt_setting;
kresult = (*(cInterface->interface))->GetAlternateSetting (cInterface->interface, &alt_setting);
- if (kresult) {
+ if (kresult != kIOReturnSuccess) {
usbi_err (HANDLE_CTX (dev_handle), "can't get alternate setting for interface");
return darwin_to_libusb (kresult);
}
cInterface->endpoint_addrs[i - 1] = endpoint_desc->bEndpointAddress;
} else {
- cInterface->endpoint_addrs[i - 1] = (((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK));
+ cInterface->endpoint_addrs[i - 1] = (UInt8)(((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK));
}
usbi_dbg ("interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift,
cInterface->num_endpoints = numep;
- return 0;
+ return LIBUSB_SUCCESS;
}
-static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface) {
+static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface) {
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
- struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+ struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
io_service_t usbInterface = IO_OBJECT_NULL;
- IOReturn kresult;
+ IOReturn kresult;
+ enum libusb_error ret;
IOCFPlugInInterface **plugInInterface = NULL;
SInt32 score;
usbi_info (HANDLE_CTX (dev_handle), "no interface found; setting configuration: %d", dpriv->first_config);
/* set the configuration */
- kresult = darwin_set_configuration (dev_handle, dpriv->first_config);
- if (kresult != LIBUSB_SUCCESS) {
+ ret = darwin_set_configuration (dev_handle, (int) dpriv->first_config);
+ if (ret != LIBUSB_SUCCESS) {
usbi_err (HANDLE_CTX (dev_handle), "could not set configuration");
- return kresult;
+ return ret;
}
kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
- if (kresult) {
+ if (kresult != kIOReturnSuccess) {
usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
/* ignore release error */
(void)IOObjectRelease (usbInterface);
- if (kresult) {
+ if (kresult != kIOReturnSuccess) {
usbi_err (HANDLE_CTX (dev_handle), "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
/* Do the actual claim */
kresult = (*plugInInterface)->QueryInterface(plugInInterface,
- CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
+ CFUUIDGetUUIDBytes(InterfaceInterfaceID),
(LPVOID)&cInterface->interface);
/* We no longer need the intermediate plug-in */
/* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */
(*plugInInterface)->Release (plugInInterface);
- if (kresult || !cInterface->interface) {
+ if (kresult != kIOReturnSuccess || !cInterface->interface) {
usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
/* claim the interface */
kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface);
- if (kresult) {
+ if (kresult != kIOReturnSuccess) {
usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceOpen: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
/* update list of endpoints */
- kresult = get_endpoints (dev_handle, iface);
- if (kresult) {
+ ret = get_endpoints (dev_handle, iface);
+ if (ret) {
/* this should not happen */
darwin_release_interface (dev_handle, iface);
usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
- return kresult;
+ return ret;
}
cInterface->cfSource = NULL;
usbi_dbg ("interface opened");
- return 0;
+ return LIBUSB_SUCCESS;
}
-static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface) {
- struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+static int darwin_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface) {
+ struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
IOReturn kresult;
/* current interface */
}
kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface);
- if (kresult)
+ if (kresult != kIOReturnSuccess)
usbi_warn (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult));
kresult = (*(cInterface->interface))->Release(cInterface->interface);
return darwin_to_libusb (kresult);
}
-static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) {
- struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting) {
+ struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
IOReturn kresult;
+ enum libusb_error ret;
/* current interface */
struct darwin_interface *cInterface = &priv->interfaces[iface];
darwin_reset_device (dev_handle);
/* update list of endpoints */
- kresult = get_endpoints (dev_handle, iface);
- if (kresult) {
+ ret = get_endpoints (dev_handle, iface);
+ if (ret) {
/* this should not happen */
darwin_release_interface (dev_handle, iface);
usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
- return kresult;
+ return ret;
}
return darwin_to_libusb (kresult);
/* newer versions of darwin support clearing additional bits on the device's endpoint */
kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef);
- if (kresult)
+ if (kresult != kIOReturnSuccess)
usbi_warn (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (kresult));
return darwin_to_libusb (kresult);
}
+static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t active_config,
+ unsigned long claimed_interfaces) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
+ int open_count = dpriv->open_count;
+ int ret;
+
+ /* clear claimed interfaces temporarily */
+ dev_handle->claimed_interfaces = 0;
+
+ /* close and re-open the device */
+ priv->is_open = false;
+ dpriv->open_count = 1;
+
+ /* clean up open interfaces */
+ (void) darwin_close (dev_handle);
+
+ /* re-open the device */
+ ret = darwin_open (dev_handle);
+ dpriv->open_count = open_count;
+ if (LIBUSB_SUCCESS != ret) {
+ /* could not restore configuration */
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ if (dpriv->active_config != active_config) {
+ usbi_dbg ("darwin/restore_state: restoring configuration %d...", active_config);
+
+ ret = darwin_set_configuration (dev_handle, active_config);
+ if (LIBUSB_SUCCESS != ret) {
+ usbi_dbg ("darwin/restore_state: could not restore configuration");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+ }
+
+ usbi_dbg ("darwin/restore_state: reclaiming interfaces");
+
+ if (claimed_interfaces) {
+ for (uint8_t iface = 0 ; iface < USB_MAXINTERFACES ; ++iface) {
+ if (!(claimed_interfaces & (1U << iface))) {
+ continue;
+ }
+
+ usbi_dbg ("darwin/restore_state: re-claiming interface %u", iface);
+
+ ret = darwin_claim_interface (dev_handle, iface);
+ if (LIBUSB_SUCCESS != ret) {
+ usbi_dbg ("darwin/restore_state: could not claim interface %u", iface);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ dev_handle->claimed_interfaces |= 1U << iface;
+ }
+ }
+
+ usbi_dbg ("darwin/restore_state: device state restored");
+
+ return LIBUSB_SUCCESS;
+}
+
static int darwin_reset_device(struct libusb_device_handle *dev_handle) {
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ unsigned long claimed_interfaces = dev_handle->claimed_interfaces;
+ int8_t active_config = dpriv->active_config;
IOUSBDeviceDescriptor descriptor;
IOUSBConfigurationDescriptorPtr cached_configuration;
- IOUSBConfigurationDescriptor configuration;
- bool reenumerate = false;
+ IOUSBConfigurationDescriptor *cached_configurations;
IOReturn kresult;
- int i;
+ UInt8 i;
- kresult = (*(dpriv->device))->ResetDevice (dpriv->device);
- if (kresult) {
- usbi_err (HANDLE_CTX (dev_handle), "ResetDevice: %s", darwin_error_str (kresult));
- return darwin_to_libusb (kresult);
+ if (dpriv->in_reenumerate) {
+ /* ack, two (or more) threads are trying to reset the device! abort! */
+ return LIBUSB_ERROR_NOT_FOUND;
}
- do {
- usbi_dbg ("darwin/reset_device: checking if device descriptor changed");
+ dpriv->in_reenumerate = true;
- /* ignore return code. if we can't get a descriptor it might be worthwhile re-enumerating anway */
- (void) darwin_request_descriptor (dpriv->device, kUSBDeviceDesc, 0, &descriptor, sizeof (descriptor));
+ /* store copies of descriptors so they can be compared after the reset */
+ memcpy (&descriptor, &dpriv->dev_descriptor, sizeof (descriptor));
+ cached_configurations = alloca (sizeof (*cached_configurations) * descriptor.bNumConfigurations);
- /* check if the device descriptor has changed */
- if (0 != memcmp (&dpriv->dev_descriptor, &descriptor, sizeof (descriptor))) {
- reenumerate = true;
- break;
- }
+ for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) {
+ (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
+ memcpy (cached_configurations + i, cached_configuration, sizeof (cached_configurations[i]));
+ }
- /* check if any configuration descriptor has changed */
- for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) {
- usbi_dbg ("darwin/reset_device: checking if configuration descriptor %d changed", i);
+ /* from macOS 10.11 ResetDevice no longer does anything so just use USBDeviceReEnumerate */
+ kresult = (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0);
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (HANDLE_CTX (dev_handle), "USBDeviceReEnumerate: %s", darwin_error_str (kresult));
+ dpriv->in_reenumerate = false;
+ return darwin_to_libusb (kresult);
+ }
- (void) darwin_request_descriptor (dpriv->device, kUSBConfDesc, i, &configuration, sizeof (configuration));
- (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
+ usbi_dbg ("darwin/reset_device: waiting for re-enumeration to complete...");
- if (!cached_configuration || 0 != memcmp (cached_configuration, &configuration, sizeof (configuration))) {
- reenumerate = true;
- break;
- }
- }
- } while (0);
+ while (dpriv->in_reenumerate) {
+ struct timespec delay = {.tv_sec = 0, .tv_nsec = 1000};
+ nanosleep (&delay, NULL);
+ }
+
+ /* compare descriptors */
+ usbi_dbg ("darwin/reset_device: checking whether descriptors changed");
- if (reenumerate) {
- usbi_dbg ("darwin/reset_device: device requires reenumeration");
- (void) (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0);
+ if (memcmp (&descriptor, &dpriv->dev_descriptor, sizeof (descriptor))) {
+ /* device descriptor changed. need to return not found. */
+ usbi_dbg ("darwin/reset_device: device descriptor changed");
return LIBUSB_ERROR_NOT_FOUND;
}
- usbi_dbg ("darwin/reset_device: device reset complete");
+ for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) {
+ (void) (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
+ if (memcmp (cached_configuration, cached_configurations + i, sizeof (cached_configurations[i]))) {
+ usbi_dbg ("darwin/reset_device: configuration descriptor %d changed", i);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+ }
- return LIBUSB_SUCCESS;
+ usbi_dbg ("darwin/reset_device: device reset complete. restoring state...");
+
+ return darwin_restore_state (dev_handle, active_config, claimed_interfaces);
}
-static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, int interface) {
+static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t interface) {
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
io_service_t usbInterface;
CFTypeRef driver;
IOReturn kresult;
kresult = darwin_get_interface (dpriv->device, interface, &usbInterface);
- if (kresult) {
+ if (kresult != kIOReturnSuccess) {
usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
return 0;
}
-/* attaching/detaching kernel drivers is not currently supported (maybe in the future?) */
-static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) {
- UNUSED(dev_handle);
- UNUSED(interface);
- return LIBUSB_ERROR_NOT_SUPPORTED;
-}
-
-static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) {
- UNUSED(dev_handle);
- UNUSED(interface);
- return LIBUSB_ERROR_NOT_SUPPORTED;
-}
-
static void darwin_destroy_device(struct libusb_device *dev) {
- struct darwin_device_priv *dpriv = (struct darwin_device_priv *) dev->os_priv;
+ struct darwin_device_priv *dpriv = usbi_get_device_priv(dev);
if (dpriv->dev) {
/* need to hold the lock in case this is the last reference to the device */
IOReturn ret;
uint8_t transferType;
- /* None of the values below are used in libusbx for bulk transfers */
- uint8_t direction, number, interval, pipeRef;
+ uint8_t pipeRef;
uint16_t maxPacketSize;
struct darwin_interface *cInterface;
+#if InterfaceVersion >= 550
+ IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3};
+#else
+ /* None of the values below are used in libusb for bulk transfers */
+ uint8_t direction, number, interval;
+#endif
if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
return LIBUSB_ERROR_NOT_FOUND;
}
+#if InterfaceVersion >= 550
+ ret = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, pipeRef, &pipeProperties);
+
+ transferType = pipeProperties.bTransferType;
+ maxPacketSize = pipeProperties.wMaxPacketSize;
+#else
ret = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
&transferType, &maxPacketSize, &interval);
+#endif
if (ret) {
usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
if (transferType == kUSBInterrupt) {
if (IS_XFERIN(transfer))
ret = (*(cInterface->interface))->ReadPipeAsync(cInterface->interface, pipeRef, transfer->buffer,
- transfer->length, darwin_async_io_callback, itransfer);
+ (UInt32)transfer->length, darwin_async_io_callback, itransfer);
else
ret = (*(cInterface->interface))->WritePipeAsync(cInterface->interface, pipeRef, transfer->buffer,
- transfer->length, darwin_async_io_callback, itransfer);
+ (UInt32)transfer->length, darwin_async_io_callback, itransfer);
} else {
itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
if (IS_XFERIN(transfer))
ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer,
- transfer->length, transfer->timeout, transfer->timeout,
- darwin_async_io_callback, (void *)itransfer);
+ (UInt32)transfer->length, transfer->timeout, transfer->timeout,
+ darwin_async_io_callback, itransfer);
else
ret = (*(cInterface->interface))->WritePipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer,
- transfer->length, transfer->timeout, transfer->timeout,
- darwin_async_io_callback, (void *)itransfer);
+ (UInt32)transfer->length, transfer->timeout, transfer->timeout,
+ darwin_async_io_callback, itransfer);
}
if (ret)
if (IS_XFERIN(transfer))
ret = (*(cInterface->interface))->ReadStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id,
- transfer->buffer, transfer->length, transfer->timeout,
- transfer->timeout, darwin_async_io_callback, (void *)itransfer);
+ transfer->buffer, (UInt32)transfer->length, transfer->timeout,
+ transfer->timeout, darwin_async_io_callback, itransfer);
else
ret = (*(cInterface->interface))->WriteStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id,
- transfer->buffer, transfer->length, transfer->timeout,
- transfer->timeout, darwin_async_io_callback, (void *)itransfer);
+ transfer->buffer, (UInt32)transfer->length, transfer->timeout,
+ transfer->timeout, darwin_async_io_callback, itransfer);
if (ret)
usbi_err (TRANSFER_CTX (transfer), "bulk stream transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
static int submit_iso_transfer(struct usbi_transfer *itransfer) {
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
IOReturn kresult;
uint8_t direction, number, interval, pipeRef, transferType;
struct darwin_interface *cInterface;
- /* construct an array of IOUSBIsocFrames, reuse the old one if possible */
- if (tpriv->isoc_framelist && tpriv->num_iso_packets != transfer->num_iso_packets) {
+ /* construct an array of IOUSBIsocFrames, reuse the old one if the sizes are the same */
+ if (tpriv->num_iso_packets != transfer->num_iso_packets) {
free(tpriv->isoc_framelist);
tpriv->isoc_framelist = NULL;
}
if (!tpriv->isoc_framelist) {
tpriv->num_iso_packets = transfer->num_iso_packets;
- tpriv->isoc_framelist = (IOUSBIsocFrame*) calloc (transfer->num_iso_packets, sizeof(IOUSBIsocFrame));
+ tpriv->isoc_framelist = (IOUSBIsocFrame*) calloc ((size_t)transfer->num_iso_packets, sizeof(IOUSBIsocFrame));
if (!tpriv->isoc_framelist)
return LIBUSB_ERROR_NO_MEM;
}
/* copy the frame list from the libusb descriptor (the structures differ only is member order) */
- for (i = 0 ; i < transfer->num_iso_packets ; i++)
- tpriv->isoc_framelist[i].frReqCount = transfer->iso_packet_desc[i].length;
+ for (i = 0 ; i < transfer->num_iso_packets ; i++) {
+ unsigned int length = transfer->iso_packet_desc[i].length;
+ assert(length <= UINT16_MAX);
+ tpriv->isoc_framelist[i].frReqCount = (UInt16)length;
+ }
/* determine the interface/endpoint to use */
if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
/* Last but not least we need the bus frame number */
kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime);
- if (kresult) {
+ if (kresult != kIOReturnSuccess) {
usbi_err (TRANSFER_CTX (transfer), "failed to get bus frame number: %d", kresult);
free(tpriv->isoc_framelist);
tpriv->isoc_framelist = NULL;
/* submit the request */
if (IS_XFERIN(transfer))
kresult = (*(cInterface->interface))->ReadIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame,
- transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
+ (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
itransfer);
else
kresult = (*(cInterface->interface))->WriteIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame,
- transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
+ (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
itransfer);
if (LIBUSB_SPEED_FULL == transfer->dev_handle->dev->speed)
/* Full speed */
- cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1));
+ cInterface->frames[transfer->endpoint] = frame + (UInt32)transfer->num_iso_packets * (1U << (interval - 1));
else
/* High/super speed */
- cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1)) / 8;
+ cInterface->frames[transfer->endpoint] = frame + (UInt32)transfer->num_iso_packets * (1U << (interval - 1)) / 8;
if (kresult != kIOReturnSuccess) {
usbi_err (TRANSFER_CTX (transfer), "isochronous transfer failed (dir: %s): %s", IS_XFERIN(transfer) ? "In" : "Out",
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct libusb_control_setup *setup = (struct libusb_control_setup *) transfer->buffer;
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev);
- struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
IOReturn kresult;
}
}
-static void darwin_clear_transfer_priv (struct usbi_transfer *itransfer) {
- struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
-
- if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS && tpriv->isoc_framelist) {
- free (tpriv->isoc_framelist);
- tpriv->isoc_framelist = NULL;
- }
-}
-
static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) {
struct usbi_transfer *itransfer = (struct usbi_transfer *)refcon;
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
usbi_dbg ("an async io operation has completed");
usbi_signal_transfer_completion(itransfer);
}
-static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_t result) {
+static enum libusb_transfer_status darwin_transfer_status (struct usbi_transfer *itransfer, IOReturn result) {
if (itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT)
result = kIOUSBTransactionTimeout;
static int darwin_handle_transfer_completion (struct usbi_transfer *itransfer) {
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
- int isIsoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type;
- int isBulk = LIBUSB_TRANSFER_TYPE_BULK == transfer->type;
- int isControl = LIBUSB_TRANSFER_TYPE_CONTROL == transfer->type;
- int isInterrupt = LIBUSB_TRANSFER_TYPE_INTERRUPT == transfer->type;
- int i;
+ struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
+ const unsigned char max_transfer_type = LIBUSB_TRANSFER_TYPE_BULK_STREAM;
+ const char *transfer_types[max_transfer_type + 1] = {"control", "isoc", "bulk", "interrupt", "bulk-stream"};
+ bool is_isoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type;
- if (!isIsoc && !isBulk && !isControl && !isInterrupt) {
+ if (transfer->type > max_transfer_type) {
usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
return LIBUSB_ERROR_INVALID_PARAM;
}
- usbi_dbg ("handling %s completion with kernel status %d",
- isControl ? "control" : isBulk ? "bulk" : isIsoc ? "isoc" : "interrupt", tpriv->result);
+ if (NULL == tpriv) {
+ usbi_err (TRANSFER_CTX(transfer), "malformed request is missing transfer priv");
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ usbi_dbg ("handling transfer completion type %s with kernel status %d", transfer_types[transfer->type], tpriv->result);
if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result) {
- if (isIsoc && tpriv->isoc_framelist) {
+ if (is_isoc && tpriv->isoc_framelist) {
/* copy isochronous results back */
- for (i = 0; i < transfer->num_iso_packets ; i++) {
+ for (int i = 0; i < transfer->num_iso_packets ; i++) {
struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i];
- lib_desc->status = darwin_to_libusb (tpriv->isoc_framelist[i].frStatus);
+ lib_desc->status = darwin_transfer_status (itransfer, tpriv->isoc_framelist[i].frStatus);
lib_desc->actual_length = tpriv->isoc_framelist[i].frActCount;
}
- } else if (!isIsoc)
+ } else if (!is_isoc) {
itransfer->transferred += tpriv->size;
+ }
}
/* it is ok to handle cancelled transfers without calling usbi_handle_transfer_cancellation (we catch timeout transfers) */
return usbi_handle_transfer_completion (itransfer, darwin_transfer_status (itransfer, tpriv->result));
}
-static int darwin_clock_gettime(int clk_id, struct timespec *tp) {
-#if !OSX_USE_CLOCK_GETTIME
+#if !defined(HAVE_CLOCK_GETTIME)
+void usbi_get_monotonic_time(struct timespec *tp) {
mach_timespec_t sys_time;
- clock_serv_t clock_ref;
-
- switch (clk_id) {
- case USBI_CLOCK_REALTIME:
- /* CLOCK_REALTIME represents time since the epoch */
- clock_ref = clock_realtime;
- break;
- case USBI_CLOCK_MONOTONIC:
- /* use system boot time as reference for the monotonic clock */
- clock_ref = clock_monotonic;
- break;
- default:
- return LIBUSB_ERROR_INVALID_PARAM;
- }
- clock_get_time (clock_ref, &sys_time);
+ /* use system boot time as reference for the monotonic clock */
+ clock_get_time (clock_monotonic, &sys_time);
tp->tv_sec = sys_time.tv_sec;
tp->tv_nsec = sys_time.tv_nsec;
+}
- return 0;
-#else
- switch (clk_id) {
- case USBI_CLOCK_MONOTONIC:
- return clock_gettime(CLOCK_MONOTONIC, tp);
- case USBI_CLOCK_REALTIME:
- return clock_gettime(CLOCK_REALTIME, tp);
- default:
- return LIBUSB_ERROR_INVALID_PARAM;
- }
-#endif
+void usbi_get_real_time(struct timespec *tp) {
+ mach_timespec_t sys_time;
+
+ /* CLOCK_REALTIME represents time since the epoch */
+ clock_get_time (clock_realtime, &sys_time);
+
+ tp->tv_sec = sys_time.tv_sec;
+ tp->tv_nsec = sys_time.tv_nsec;
}
+#endif
#if InterfaceVersion >= 550
static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32_t num_streams, unsigned char *endpoints,
uint8_t pipeRef;
int rc, i;
- /* find the mimimum number of supported streams on the endpoint list */
+ /* find the minimum number of supported streams on the endpoint list */
for (i = 0 ; i < num_endpoints ; ++i) {
if (0 != (rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface))) {
return rc;
return darwin_to_libusb(rc);
}
- return num_streams;
+ assert(num_streams <= INT_MAX);
+ return (int)num_streams;
}
static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigned char *endpoints, int num_endpoints) {
.caps = 0,
.init = darwin_init,
.exit = darwin_exit,
- .get_device_list = NULL, /* not needed */
- .get_device_descriptor = darwin_get_device_descriptor,
.get_active_config_descriptor = darwin_get_active_config_descriptor,
.get_config_descriptor = darwin_get_config_descriptor,
.hotplug_poll = darwin_hotplug_poll,
#endif
.kernel_driver_active = darwin_kernel_driver_active,
- .detach_kernel_driver = darwin_detach_kernel_driver,
- .attach_kernel_driver = darwin_attach_kernel_driver,
.destroy_device = darwin_destroy_device,
.submit_transfer = darwin_submit_transfer,
.cancel_transfer = darwin_cancel_transfer,
- .clear_transfer_priv = darwin_clear_transfer_priv,
.handle_transfer_completion = darwin_handle_transfer_completion,
- .clock_gettime = darwin_clock_gettime,
-
.device_priv_size = sizeof(struct darwin_device_priv),
.device_handle_priv_size = sizeof(struct darwin_device_handle_priv),
.transfer_priv_size = sizeof(struct darwin_transfer_priv),
/*
* darwin backend for libusb 1.0
- * Copyright © 2008-2015 Nathan Hjelm <hjelmn@users.sourceforge.net>
+ * Copyright © 2008-2019 Nathan Hjelm <hjelmn@users.sourceforge.net>
+ * Copyright © 2019 Google LLC. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#if !defined(LIBUSB_DARWIN_H)
#define LIBUSB_DARWIN_H
+#include <stdbool.h>
+
#include "libusbi.h"
#include <IOKit/IOTypes.h>
UInt32 location;
UInt64 parent_session;
UInt64 session;
- UInt16 address;
+ USBDeviceAddress address;
char sys_path[21];
usb_device_t **device;
int open_count;
- UInt8 first_config, active_config, port;
+ UInt8 first_config, active_config, port;
int can_enumerate;
int refcount;
+ bool in_reenumerate;
};
struct darwin_device_priv {
};
struct darwin_device_handle_priv {
- int is_open;
+ bool is_open;
CFRunLoopSourceRef cfSource;
struct darwin_interface {
--- /dev/null
+/*
+ * libusb event abstraction on POSIX platforms
+ *
+ * Copyright © 2020 Chris Dickens <christopher.a.dickens@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libusbi.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_EVENTFD
+#include <sys/eventfd.h>
+#endif
+#ifdef HAVE_TIMERFD
+#include <sys/timerfd.h>
+#endif
+#include <unistd.h>
+
+#ifdef HAVE_EVENTFD
+#define EVENT_READ_FD(e) ((e)->eventfd)
+#define EVENT_WRITE_FD(e) ((e)->eventfd)
+#else
+#define EVENT_READ_FD(e) ((e)->pipefd[0])
+#define EVENT_WRITE_FD(e) ((e)->pipefd[1])
+#endif
+
+#ifdef HAVE_NFDS_T
+typedef nfds_t usbi_nfds_t;
+#else
+typedef unsigned int usbi_nfds_t;
+#endif
+
+int usbi_create_event(usbi_event_t *event)
+{
+#ifdef HAVE_EVENTFD
+ event->eventfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
+ if (event->eventfd == -1) {
+ usbi_err(NULL, "failed to create eventfd, errno=%d", errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+#else
+#if defined(HAVE_PIPE2)
+ int ret = pipe2(event->pipefd, O_CLOEXEC);
+#else
+ int ret = pipe(event->pipefd);
+#endif
+
+ if (ret != 0) {
+ usbi_err(NULL, "failed to create pipe, errno=%d", errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+#if !defined(HAVE_PIPE2) && defined(FD_CLOEXEC)
+ ret = fcntl(event->pipefd[0], F_GETFD);
+ if (ret == -1) {
+ usbi_err(NULL, "failed to get pipe fd flags, errno=%d", errno);
+ goto err_close_pipe;
+ }
+ ret = fcntl(event->pipefd[0], F_SETFD, ret | FD_CLOEXEC);
+ if (ret == -1) {
+ usbi_err(NULL, "failed to set pipe fd flags, errno=%d", errno);
+ goto err_close_pipe;
+ }
+
+ ret = fcntl(event->pipefd[1], F_GETFD);
+ if (ret == -1) {
+ usbi_err(NULL, "failed to get pipe fd flags, errno=%d", errno);
+ goto err_close_pipe;
+ }
+ ret = fcntl(event->pipefd[1], F_SETFD, ret | FD_CLOEXEC);
+ if (ret == -1) {
+ usbi_err(NULL, "failed to set pipe fd flags, errno=%d", errno);
+ goto err_close_pipe;
+ }
+#endif
+
+ ret = fcntl(event->pipefd[1], F_GETFL);
+ if (ret == -1) {
+ usbi_err(NULL, "failed to get pipe fd status flags, errno=%d", errno);
+ goto err_close_pipe;
+ }
+ ret = fcntl(event->pipefd[1], F_SETFL, ret | O_NONBLOCK);
+ if (ret == -1) {
+ usbi_err(NULL, "failed to set pipe fd status flags, errno=%d", errno);
+ goto err_close_pipe;
+ }
+
+ return 0;
+
+err_close_pipe:
+ close(event->pipefd[1]);
+ close(event->pipefd[0]);
+ return LIBUSB_ERROR_OTHER;
+#endif
+}
+
+void usbi_destroy_event(usbi_event_t *event)
+{
+#ifdef HAVE_EVENTFD
+ if (close(event->eventfd) == -1)
+ usbi_warn(NULL, "failed to close eventfd, errno=%d", errno);
+#else
+ if (close(event->pipefd[1]) == -1)
+ usbi_warn(NULL, "failed to close pipe write end, errno=%d", errno);
+ if (close(event->pipefd[0]) == -1)
+ usbi_warn(NULL, "failed to close pipe read end, errno=%d", errno);
+#endif
+}
+
+void usbi_signal_event(usbi_event_t *event)
+{
+ uint64_t dummy = 1;
+ ssize_t r;
+
+ r = write(EVENT_WRITE_FD(event), &dummy, sizeof(dummy));
+ if (r != sizeof(dummy))
+ usbi_warn(NULL, "event write failed");
+}
+
+void usbi_clear_event(usbi_event_t *event)
+{
+ uint64_t dummy;
+ ssize_t r;
+
+ r = read(EVENT_READ_FD(event), &dummy, sizeof(dummy));
+ if (r != sizeof(dummy))
+ usbi_warn(NULL, "event read failed");
+}
+
+#ifdef HAVE_TIMERFD
+int usbi_create_timer(usbi_timer_t *timer)
+{
+ timer->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
+ if (timer->timerfd == -1) {
+ usbi_warn(NULL, "failed to create timerfd, errno=%d", errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+
+void usbi_destroy_timer(usbi_timer_t *timer)
+{
+ if (close(timer->timerfd) == -1)
+ usbi_warn(NULL, "failed to close timerfd, errno=%d", errno);
+}
+
+int usbi_arm_timer(usbi_timer_t *timer, const struct timespec *timeout)
+{
+ const struct itimerspec it = { { 0, 0 }, { timeout->tv_sec, timeout->tv_nsec } };
+
+ if (timerfd_settime(timer->timerfd, TFD_TIMER_ABSTIME, &it, NULL) == -1) {
+ usbi_warn(NULL, "failed to arm timerfd, errno=%d", errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+
+int usbi_disarm_timer(usbi_timer_t *timer)
+{
+ const struct itimerspec it = { { 0, 0 }, { 0, 0 } };
+
+ if (timerfd_settime(timer->timerfd, 0, &it, NULL) == -1) {
+ usbi_warn(NULL, "failed to disarm timerfd, errno=%d", errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+#endif
+
+int usbi_alloc_event_data(struct libusb_context *ctx)
+{
+ struct usbi_event_source *ievent_source;
+ struct pollfd *fds;
+ size_t i = 0;
+
+ if (ctx->event_data) {
+ free(ctx->event_data);
+ ctx->event_data = NULL;
+ }
+
+ ctx->event_data_cnt = 0;
+ for_each_event_source(ctx, ievent_source)
+ ctx->event_data_cnt++;
+
+ fds = calloc(ctx->event_data_cnt, sizeof(*fds));
+ if (!fds)
+ return LIBUSB_ERROR_NO_MEM;
+
+ for_each_event_source(ctx, ievent_source) {
+ fds[i].fd = ievent_source->data.os_handle;
+ fds[i].events = ievent_source->data.poll_events;
+ i++;
+ }
+
+ ctx->event_data = fds;
+ return 0;
+}
+
+int usbi_wait_for_events(struct libusb_context *ctx,
+ struct usbi_reported_events *reported_events, int timeout_ms)
+{
+ struct pollfd *fds = ctx->event_data;
+ usbi_nfds_t nfds = (usbi_nfds_t)ctx->event_data_cnt;
+ int internal_fds, num_ready;
+
+ usbi_dbg("poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms);
+ num_ready = poll(fds, nfds, timeout_ms);
+ usbi_dbg("poll() returned %d", num_ready);
+ if (num_ready == 0) {
+ if (usbi_using_timer(ctx))
+ goto done;
+ return LIBUSB_ERROR_TIMEOUT;
+ } else if (num_ready == -1) {
+ if (errno == EINTR)
+ return LIBUSB_ERROR_INTERRUPTED;
+ usbi_err(ctx, "poll() failed, errno=%d", errno);
+ return LIBUSB_ERROR_IO;
+ }
+
+ /* fds[0] is always the internal signalling event */
+ if (fds[0].revents) {
+ reported_events->event_triggered = 1;
+ num_ready--;
+ } else {
+ reported_events->event_triggered = 0;
+ }
+
+#ifdef HAVE_OS_TIMER
+ /* on timer configurations, fds[1] is the timer */
+ if (usbi_using_timer(ctx) && fds[1].revents) {
+ reported_events->timer_triggered = 1;
+ num_ready--;
+ } else {
+ reported_events->timer_triggered = 0;
+ }
+#endif
+
+ if (!num_ready)
+ goto done;
+
+ /* the backend will never need to attempt to handle events on the
+ * library's internal file descriptors, so we determine how many are
+ * in use internally for this context and skip these when passing any
+ * remaining pollfds to the backend. */
+ internal_fds = usbi_using_timer(ctx) ? 2 : 1;
+ fds += internal_fds;
+ nfds -= internal_fds;
+
+ usbi_mutex_lock(&ctx->event_data_lock);
+ if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) {
+ struct usbi_event_source *ievent_source;
+
+ for_each_removed_event_source(ctx, ievent_source) {
+ usbi_nfds_t n;
+
+ for (n = 0; n < nfds; n++) {
+ if (ievent_source->data.os_handle != fds[n].fd)
+ continue;
+ if (!fds[n].revents)
+ continue;
+ /* pollfd was removed between the creation of the fds array and
+ * here. remove triggered revent as it is no longer relevant. */
+ usbi_dbg("fd %d was removed, ignoring raised events", fds[n].fd);
+ fds[n].revents = 0;
+ num_ready--;
+ break;
+ }
+ }
+ }
+ usbi_mutex_unlock(&ctx->event_data_lock);
+
+ if (num_ready) {
+ assert(num_ready > 0);
+ reported_events->event_data = fds;
+ reported_events->event_data_count = (unsigned int)nfds;
+ }
+
+done:
+ reported_events->num_ready = num_ready;
+ return LIBUSB_SUCCESS;
+}
--- /dev/null
+/*
+ * libusb event abstraction on POSIX platforms
+ *
+ * Copyright © 2020 Chris Dickens <christopher.a.dickens@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LIBUSB_EVENTS_POSIX_H
+#define LIBUSB_EVENTS_POSIX_H
+
+#include <poll.h>
+
+typedef int usbi_os_handle_t;
+#define USBI_OS_HANDLE_FORMAT_STRING "fd %d"
+
+#ifdef HAVE_EVENTFD
+typedef struct usbi_event {
+ int eventfd;
+} usbi_event_t;
+#define USBI_EVENT_OS_HANDLE(e) ((e)->eventfd)
+#define USBI_EVENT_POLL_EVENTS POLLIN
+#define USBI_INVALID_EVENT { -1 }
+#else
+typedef struct usbi_event {
+ int pipefd[2];
+} usbi_event_t;
+#define USBI_EVENT_OS_HANDLE(e) ((e)->pipefd[0])
+#define USBI_EVENT_POLL_EVENTS POLLIN
+#define USBI_INVALID_EVENT { { -1, -1 } }
+#endif
+
+#ifdef HAVE_TIMERFD
+#define HAVE_OS_TIMER 1
+typedef struct usbi_timer {
+ int timerfd;
+} usbi_timer_t;
+#define USBI_TIMER_OS_HANDLE(t) ((t)->timerfd)
+#define USBI_TIMER_POLL_EVENTS POLLIN
+
+static inline int usbi_timer_valid(usbi_timer_t *timer)
+{
+ return timer->timerfd >= 0;
+}
+#endif
+
+#endif
+++ /dev/null
-/*
- * poll_posix: poll compatibility wrapper for POSIX systems
- * Copyright © 2013 RealVNC Ltd.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include <config.h>
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdlib.h>
-
-#include "libusbi.h"
-
-int usbi_pipe(int pipefd[2])
-{
-#if defined(HAVE_PIPE2)
- int ret = pipe2(pipefd, O_CLOEXEC);
-#else
- int ret = pipe(pipefd);
-#endif
-
- if (ret != 0) {
- usbi_err(NULL, "failed to create pipe (%d)", errno);
- return ret;
- }
-
-#if !defined(HAVE_PIPE2) && defined(FD_CLOEXEC)
- ret = fcntl(pipefd[0], F_GETFD);
- if (ret == -1) {
- usbi_err(NULL, "failed to get pipe fd flags (%d)", errno);
- goto err_close_pipe;
- }
- ret = fcntl(pipefd[0], F_SETFD, ret | FD_CLOEXEC);
- if (ret == -1) {
- usbi_err(NULL, "failed to set pipe fd flags (%d)", errno);
- goto err_close_pipe;
- }
-
- ret = fcntl(pipefd[1], F_GETFD);
- if (ret == -1) {
- usbi_err(NULL, "failed to get pipe fd flags (%d)", errno);
- goto err_close_pipe;
- }
- ret = fcntl(pipefd[1], F_SETFD, ret | FD_CLOEXEC);
- if (ret == -1) {
- usbi_err(NULL, "failed to set pipe fd flags (%d)", errno);
- goto err_close_pipe;
- }
-#endif
-
- ret = fcntl(pipefd[1], F_GETFL);
- if (ret == -1) {
- usbi_err(NULL, "failed to get pipe fd status flags (%d)", errno);
- goto err_close_pipe;
- }
- ret = fcntl(pipefd[1], F_SETFL, ret | O_NONBLOCK);
- if (ret == -1) {
- usbi_err(NULL, "failed to set pipe fd status flags (%d)", errno);
- goto err_close_pipe;
- }
-
- return 0;
-
-err_close_pipe:
- close(pipefd[0]);
- close(pipefd[1]);
- return ret;
-}
+++ /dev/null
-#ifndef LIBUSB_POLL_POSIX_H
-#define LIBUSB_POLL_POSIX_H
-
-#define usbi_write write
-#define usbi_read read
-#define usbi_close close
-#define usbi_poll poll
-
-int usbi_pipe(int pipefd[2]);
-
-#endif /* LIBUSB_POLL_POSIX_H */
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <config.h>
+#include "libusbi.h"
-#include <time.h>
-#if defined(__linux__) || defined(__OpenBSD__)
-# if defined(__OpenBSD__)
-# define _BSD_SOURCE
-# endif
+#include <errno.h>
+#if defined(__ANDROID__)
# include <unistd.h>
+#elif defined(__HAIKU__)
+# include <os/kernel/OS.h>
+#elif defined(__linux__)
# include <sys/syscall.h>
-#elif defined(__APPLE__)
-# include <pthread.h>
-#elif defined(__CYGWIN__)
-# include <windows.h>
+# include <unistd.h>
+#elif defined(__NetBSD__)
+# include <lwp.h>
+#elif defined(__OpenBSD__)
+# define _BSD_SOURCE
+# include <sys/syscall.h>
+# include <unistd.h>
+#elif defined(__sun__)
+# include <sys/lwp.h>
#endif
-#include "threads_posix.h"
-#include "libusbi.h"
+void usbi_cond_init(pthread_cond_t *cond)
+{
+#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK
+ pthread_condattr_t condattr;
+
+ PTHREAD_CHECK(pthread_condattr_init(&condattr));
+ PTHREAD_CHECK(pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC));
+ PTHREAD_CHECK(pthread_cond_init(cond, &condattr));
+ PTHREAD_CHECK(pthread_condattr_destroy(&condattr));
+#else
+ PTHREAD_CHECK(pthread_cond_init(cond, NULL));
+#endif
+}
int usbi_cond_timedwait(pthread_cond_t *cond,
pthread_mutex_t *mutex, const struct timeval *tv)
struct timespec timeout;
int r;
- r = usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, &timeout);
- if (r < 0)
- return r;
+#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK
+ usbi_get_monotonic_time(&timeout);
+#else
+ usbi_get_real_time(&timeout);
+#endif
timeout.tv_sec += tv->tv_sec;
- timeout.tv_nsec += tv->tv_usec * 1000;
- while (timeout.tv_nsec >= 1000000000L) {
- timeout.tv_nsec -= 1000000000L;
+ timeout.tv_nsec += tv->tv_usec * 1000L;
+ if (timeout.tv_nsec >= NSEC_PER_SEC) {
+ timeout.tv_nsec -= NSEC_PER_SEC;
timeout.tv_sec++;
}
- return pthread_cond_timedwait(cond, mutex, &timeout);
+ r = pthread_cond_timedwait(cond, mutex, &timeout);
+ if (r == 0)
+ return 0;
+ else if (r == ETIMEDOUT)
+ return LIBUSB_ERROR_TIMEOUT;
+ else
+ return LIBUSB_ERROR_OTHER;
}
-int usbi_get_tid(void)
+unsigned int usbi_get_tid(void)
{
- int ret;
+ static _Thread_local unsigned int tl_tid;
+ int tid;
+
+ if (tl_tid)
+ return tl_tid;
+
#if defined(__ANDROID__)
- ret = gettid();
+ tid = gettid();
+#elif defined(__APPLE__)
+#ifdef HAVE_PTHREAD_THREADID_NP
+ uint64_t thread_id;
+
+ if (pthread_threadid_np(NULL, &thread_id) == 0)
+ tid = (int)thread_id;
+ else
+ tid = -1;
+#else
+ tid = (int)pthread_mach_thread_np(pthread_self());
+#endif
+#elif defined(__HAIKU__)
+ tid = get_pthread_thread_id(pthread_self());
#elif defined(__linux__)
- ret = syscall(SYS_gettid);
+ tid = (int)syscall(SYS_gettid);
+#elif defined(__NetBSD__)
+ tid = _lwp_self();
#elif defined(__OpenBSD__)
/* The following only works with OpenBSD > 5.1 as it requires
- real thread support. For 5.1 and earlier, -1 is returned. */
- ret = syscall(SYS_getthrid);
-#elif defined(__APPLE__)
- ret = (int)pthread_mach_thread_np(pthread_self());
-#elif defined(__CYGWIN__)
- ret = GetCurrentThreadId();
+ * real thread support. For 5.1 and earlier, -1 is returned. */
+ tid = syscall(SYS_getthrid);
+#elif defined(__sun__)
+ tid = _lwp_self();
#else
- ret = -1;
+ tid = -1;
#endif
-/* TODO: NetBSD thread ID support */
- return ret;
+
+ if (tid == -1) {
+ /* If we don't have a thread ID, at least return a unique
+ * value that can be used to distinguish individual
+ * threads. */
+ tid = (int)(intptr_t)pthread_self();
+ }
+
+ return tl_tid = (unsigned int)tid;
}
#define LIBUSB_THREADS_POSIX_H
#include <pthread.h>
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
+
+#define PTHREAD_CHECK(expression) ASSERT_EQ(expression, 0)
#define USBI_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
typedef pthread_mutex_t usbi_mutex_static_t;
static inline void usbi_mutex_static_lock(usbi_mutex_static_t *mutex)
{
- (void)pthread_mutex_lock(mutex);
+ PTHREAD_CHECK(pthread_mutex_lock(mutex));
}
static inline void usbi_mutex_static_unlock(usbi_mutex_static_t *mutex)
{
- (void)pthread_mutex_unlock(mutex);
+ PTHREAD_CHECK(pthread_mutex_unlock(mutex));
}
typedef pthread_mutex_t usbi_mutex_t;
-static inline int usbi_mutex_init(usbi_mutex_t *mutex)
+static inline void usbi_mutex_init(usbi_mutex_t *mutex)
{
- return pthread_mutex_init(mutex, NULL);
+ PTHREAD_CHECK(pthread_mutex_init(mutex, NULL));
}
static inline void usbi_mutex_lock(usbi_mutex_t *mutex)
{
- (void)pthread_mutex_lock(mutex);
+ PTHREAD_CHECK(pthread_mutex_lock(mutex));
}
static inline void usbi_mutex_unlock(usbi_mutex_t *mutex)
{
- (void)pthread_mutex_unlock(mutex);
+ PTHREAD_CHECK(pthread_mutex_unlock(mutex));
}
static inline int usbi_mutex_trylock(usbi_mutex_t *mutex)
{
- return pthread_mutex_trylock(mutex);
+ return pthread_mutex_trylock(mutex) == 0;
}
static inline void usbi_mutex_destroy(usbi_mutex_t *mutex)
{
- (void)pthread_mutex_destroy(mutex);
+ PTHREAD_CHECK(pthread_mutex_destroy(mutex));
}
typedef pthread_cond_t usbi_cond_t;
-static inline void usbi_cond_init(pthread_cond_t *cond)
-{
- (void)pthread_cond_init(cond, NULL);
-}
-static inline int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex)
+void usbi_cond_init(pthread_cond_t *cond);
+static inline void usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex)
{
- return pthread_cond_wait(cond, mutex);
+ PTHREAD_CHECK(pthread_cond_wait(cond, mutex));
}
int usbi_cond_timedwait(usbi_cond_t *cond,
usbi_mutex_t *mutex, const struct timeval *tv);
static inline void usbi_cond_broadcast(usbi_cond_t *cond)
{
- (void)pthread_cond_broadcast(cond);
+ PTHREAD_CHECK(pthread_cond_broadcast(cond));
}
static inline void usbi_cond_destroy(usbi_cond_t *cond)
{
- (void)pthread_cond_destroy(cond);
+ PTHREAD_CHECK(pthread_cond_destroy(cond));
}
typedef pthread_key_t usbi_tls_key_t;
static inline void usbi_tls_key_create(usbi_tls_key_t *key)
{
- (void)pthread_key_create(key, NULL);
+ PTHREAD_CHECK(pthread_key_create(key, NULL));
}
static inline void *usbi_tls_key_get(usbi_tls_key_t key)
{
}
static inline void usbi_tls_key_set(usbi_tls_key_t key, void *ptr)
{
- (void)pthread_setspecific(key, ptr);
+ PTHREAD_CHECK(pthread_setspecific(key, ptr));
}
static inline void usbi_tls_key_delete(usbi_tls_key_t key)
{
- (void)pthread_key_delete(key);
+ PTHREAD_CHECK(pthread_key_delete(key));
}
-int usbi_get_tid(void);
+unsigned int usbi_get_tid(void);
#endif /* LIBUSB_THREADS_POSIX_H */
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <config.h>
-
-#include <locale.h>
-#include <stdlib.h>
-#include <string.h>
-#if defined(HAVE_STRINGS_H)
-#include <strings.h>
-#endif
-
#include "libusbi.h"
-#if defined(_MSC_VER)
-#define strncasecmp _strnicmp
-#endif
-
-static size_t usbi_locale = 0;
+#include <ctype.h>
+#include <string.h>
/** \ingroup libusb_misc
* How to add a new \ref libusb_strerror() translation:
* <ol>
* <li> Download the latest \c strerror.c from:<br>
- * https://raw.github.com/libusb/libusb/master/libusb/sterror.c </li>
+ * https://raw.github.com/libusb/libusb/master/libusb/strerror.c </li>
* <li> Open the file in an UTF-8 capable editor </li>
* <li> Add the 2 letter <a href="http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes">ISO 639-1</a>
* code for your locale at the end of \c usbi_locale_supported[]<br>
* "Success",
* ...
* "Other error",
- * }
+ * },
* };\endcode </li>
* <li> Translate each of the English messages from the section you copied into your language </li>
* <li> Save the file (in UTF-8 format) and send it to \c libusb-devel\@lists.sourceforge.net </li>
* </ol>
*/
-static const char* usbi_locale_supported[] = { "en", "nl", "fr", "ru" };
-static const char* usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUSB_ERROR_COUNT] = {
+static const char * const usbi_locale_supported[] = { "en", "nl", "fr", "ru", "de", "hu" };
+static const char * const usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUSB_ERROR_COUNT] = {
{ /* English (en) */
"Success",
"Input/Output Error",
"Память исчерпана",
"Операция не поддерживается данной платформой",
"Неизвестная ошибка"
- }
+ }, { /* German (de) */
+ "Erfolgreich",
+ "Eingabe-/Ausgabefehler",
+ "Ungültiger Parameter",
+ "Keine Berechtigung (Zugriffsrechte fehlen)",
+ "Kein passendes Gerät gefunden (es könnte entfernt worden sein)",
+ "Entität nicht gefunden",
+ "Die Ressource ist belegt",
+ "Die Wartezeit für die Operation ist abgelaufen",
+ "Mehr Daten empfangen als erwartet",
+ "Datenübergabe unterbrochen (broken pipe)",
+ "Unterbrechung während des Betriebssystemaufrufs",
+ "Nicht genügend Hauptspeicher verfügbar",
+ "Die Operation wird nicht unterstützt oder ist auf dieser Platform nicht implementiert",
+ "Allgemeiner Fehler",
+ }, { /* Hungarian (hu) */
+ "Sikeres",
+ "Be-/kimeneti hiba",
+ "Érvénytelen paraméter",
+ "Hozzáférés megtagadva",
+ "Az eszköz nem található (eltávolították?)",
+ "Nem található",
+ "Az erőforrás foglalt",
+ "Időtúllépés",
+ "Túlcsordulás",
+ "Törött adatcsatorna",
+ "Rendszerhívás megszakítva",
+ "Nincs elég memória",
+ "A művelet nem támogatott ezen a rendszeren",
+ "Általános hiba",
+ },
};
+static const char * const (*usbi_error_strings)[LIBUSB_ERROR_COUNT] = &usbi_localized_errors[0];
+
/** \ingroup libusb_misc
* Set the language, and only the language, not the encoding! used for
* translatable libusb messages.
* used, and only 2 letter ISO 639-1 codes are accepted for it, such as "de".
* The optional region, country_region or codeset parts are ignored. This
* means that functions which return translatable strings will NOT honor the
- * specified encoding.
+ * specified encoding.
* All strings returned are encoded as UTF-8 strings.
*
* If libusb_setlocale() is not called, all messages will be in English.
{
size_t i;
- if ( (locale == NULL) || (strlen(locale) < 2)
- || ((strlen(locale) > 2) && (locale[2] != '-') && (locale[2] != '_') && (locale[2] != '.')) )
+ if (!locale || strlen(locale) < 2
+ || (locale[2] != '\0' && locale[2] != '-' && locale[2] != '_' && locale[2] != '.'))
return LIBUSB_ERROR_INVALID_PARAM;
- for (i=0; i<ARRAYSIZE(usbi_locale_supported); i++) {
- if (strncasecmp(usbi_locale_supported[i], locale, 2) == 0)
+ for (i = 0; i < ARRAYSIZE(usbi_locale_supported); i++) {
+ if (usbi_locale_supported[i][0] == tolower((unsigned char)locale[0])
+ && usbi_locale_supported[i][1] == tolower((unsigned char)locale[1]))
break;
}
- if (i >= ARRAYSIZE(usbi_locale_supported)) {
+
+ if (i == ARRAYSIZE(usbi_locale_supported))
return LIBUSB_ERROR_NOT_FOUND;
- }
- usbi_locale = i;
+ usbi_error_strings = &usbi_localized_errors[i];
return LIBUSB_SUCCESS;
}
* \param errcode the error code whose description is desired
* \returns a short description of the error code in UTF-8 encoding
*/
-DEFAULT_VISIBILITY const char* LIBUSB_CALL libusb_strerror(enum libusb_error errcode)
+DEFAULT_VISIBILITY const char * LIBUSB_CALL libusb_strerror(int errcode)
{
int errcode_index = -errcode;
- if ((errcode_index < 0) || (errcode_index >= LIBUSB_ERROR_COUNT)) {
+ if (errcode_index < 0 || errcode_index >= LIBUSB_ERROR_COUNT) {
/* "Other Error", which should always be our last message, is returned */
errcode_index = LIBUSB_ERROR_COUNT - 1;
}
- return usbi_localized_errors[usbi_locale][errcode_index];
+ return (*usbi_error_strings)[errcode_index];
}
+/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
/*
* Synchronous I/O functions for libusb
* Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org>
+ * Copyright © 2019 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2019 Google LLC. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <config.h>
+#include "libusbi.h"
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
#include <string.h>
-#include "libusbi.h"
-
/**
* @defgroup libusb_syncio Synchronous device I/O
*
libusb_cancel_transfer(transfer);
continue;
}
+ if (NULL == transfer->dev_handle) {
+ /* transfer completion after libusb_close() */
+ transfer->status = LIBUSB_TRANSFER_NO_DEVICE;
+ *completed = 1;
+ }
}
}
* (depending on direction bits within bmRequestType)
* \param wLength the length field for the setup packet. The data buffer should
* be at least this size.
- * \param timeout timeout (in millseconds) that this function should wait
+ * \param timeout timeout (in milliseconds) that this function should wait
* before giving up due to no response being received. For an unlimited
* timeout, use value 0.
* \returns on success, the number of bytes actually transferred
* \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns LIBUSB_ERROR_BUSY if called from event handling context
* \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
- * the operating system and/or hardware can support
+ * the operating system and/or hardware can support (see \ref asynclimits)
* \returns another LIBUSB_ERROR code on other failures
*/
int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle,
if (!transfer)
return LIBUSB_ERROR_NO_MEM;
- buffer = (unsigned char*) malloc(LIBUSB_CONTROL_SETUP_SIZE + wLength);
+ buffer = malloc(LIBUSB_CONTROL_SETUP_SIZE + wLength);
if (!buffer) {
libusb_free_transfer(transfer);
return LIBUSB_ERROR_NO_MEM;
* underlying O/S requirements, meaning that the timeout may expire after
* the first few chunks have completed. libusb is careful not to lose any data
* that may have been transferred; do not assume that timeout conditions
- * indicate a complete lack of I/O.
+ * indicate a complete lack of I/O. See \ref asynctimeout for more details.
*
* \param dev_handle a handle for the device to communicate with
* \param endpoint the address of a valid endpoint to communicate with
* transferred. Since version 1.0.21 (\ref LIBUSB_API_VERSION >= 0x01000105),
* it is legal to pass a NULL pointer if you do not wish to receive this
* information.
- * \param timeout timeout (in millseconds) that this function should wait
+ * \param timeout timeout (in milliseconds) that this function should wait
* before giving up due to no response being received. For an unlimited
* timeout, use value 0.
*
* \ref libusb_packetoverflow
* \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns LIBUSB_ERROR_BUSY if called from event handling context
+ * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
+ * the operating system and/or hardware can support (see \ref asynclimits)
* \returns another LIBUSB_ERROR code on other failures
*/
-int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle,
- unsigned char endpoint, unsigned char *data, int length, int *transferred,
- unsigned int timeout)
+int API_EXPORTED libusb_bulk_transfer(libusb_device_handle *dev_handle,
+ unsigned char endpoint, unsigned char *data, int length,
+ int *transferred, unsigned int timeout)
{
return do_sync_bulk_transfer(dev_handle, endpoint, data, length,
transferred, timeout, LIBUSB_TRANSFER_TYPE_BULK);
* underlying O/S requirements, meaning that the timeout may expire after
* the first few chunks have completed. libusb is careful not to lose any data
* that may have been transferred; do not assume that timeout conditions
- * indicate a complete lack of I/O.
+ * indicate a complete lack of I/O. See \ref asynctimeout for more details.
*
* The default endpoint bInterval value is used as the polling interval.
*
* transferred. Since version 1.0.21 (\ref LIBUSB_API_VERSION >= 0x01000105),
* it is legal to pass a NULL pointer if you do not wish to receive this
* information.
- * \param timeout timeout (in millseconds) that this function should wait
+ * \param timeout timeout (in milliseconds) that this function should wait
* before giving up due to no response being received. For an unlimited
* timeout, use value 0.
*
* \ref libusb_packetoverflow
* \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns LIBUSB_ERROR_BUSY if called from event handling context
+ * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
+ * the operating system and/or hardware can support (see \ref asynclimits)
* \returns another LIBUSB_ERROR code on other error
*/
-int API_EXPORTED libusb_interrupt_transfer(
- struct libusb_device_handle *dev_handle, unsigned char endpoint,
- unsigned char *data, int length, int *transferred, unsigned int timeout)
+int API_EXPORTED libusb_interrupt_transfer(libusb_device_handle *dev_handle,
+ unsigned char endpoint, unsigned char *data, int length,
+ int *transferred, unsigned int timeout)
{
return do_sync_bulk_transfer(dev_handle, endpoint, data, length,
transferred, timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT);
#define LIBUSB_MINOR 0
#endif
#ifndef LIBUSB_MICRO
-#define LIBUSB_MICRO 22
+#define LIBUSB_MICRO 24
#endif
#ifndef LIBUSB_NANO
#define LIBUSB_NANO 0
-#define LIBUSB_NANO 11312
+#define LIBUSB_NANO 11584